Every script so far ran all of its statements, top to bottom. Real programs choose: free entry for small children, a warning when a number is too large. To choose, a script asks a question about its values and runs some statements only when the answer is yes.
A condition is a yes-or-no question
A condition is a question that is either true or false. In Bash you write one between [[ and ]], and if runs a block, a group of statements that belong together, only when the condition is true.
age=20
if [[ "$age" -ge 18 ]]; then
echo "You may vote."
fi
echo "Done."This prints You may vote. and then Done.. Change 20 to 15 and run it again: it prints only Done..
Read the pieces in order:
[[ "$age" -ge 18 ]]is the condition.-gemeans "is greater than or equal to", so this asks: is the age at least 18?; thenends the condition and starts the block.- The lines down to
fiare the block. They run only when the condition is true. fi("if" backwards) ends the block. The statements after it run in every case.
The two spaces at the start of the line inside the block are called indentation. Bash does not need them; people do. Indent every block so that a reader can see at a glance which lines belong to the if.
The spaces inside the double square brackets are required: one after [[, one before ]], and one on each side of -ge.
Comparing numbers
Six operators compare whole numbers. Each is a dash and two letters:
| Operator | Meaning | True example |
|---|---|---|
-eq |
equal | [[ 5 -eq 5 ]] |
-ne |
not equal | [[ 5 -ne 3 ]] |
-lt |
less than | [[ 3 -lt 5 ]] |
-le |
less than or equal | [[ 5 -le 5 ]] |
-gt |
greater than | [[ 5 -gt 3 ]] |
-ge |
greater than or equal | [[ 5 -ge 5 ]] |
The difference between -lt and -le, and between -gt and -ge, is what happens when the two numbers are equal. "Under 18" is -lt 18; "18 or under" is -le 18. Decide which one the task means, every time.
Comparing text
Text is compared with == (the same) and != (different):
answer="yes"
if [[ "$answer" == "yes" ]]; then
echo "Saving."
fi
if [[ "$answer" != "no" ]]; then
echo "The answer was not no."
fiThis prints Saving. and The answer was not no..
== compares character by character, and capital letters count: "Yes" and "yes" are different. Note the two equals signs. One = assigns; two compare.
Why there are two sets of operators
To Bash every value is text, so the operator has to say how to compare. -eq and its relatives read both sides as numbers. == compares the characters.
if [[ "07" -eq "7" ]]; then
echo "-eq: the same number"
fi
if [[ "07" == "7" ]]; then
echo "==: the same text"
else
echo "==: different text"
fiThis prints -eq: the same number and ==: different text. Use the dash operators for numbers and == for words.
Two traps follow from this, and neither gives an error message:
<and>do not compare numbers inside[[ ]]. They compare text in dictionary order, where"9"comes after"10"because the character 9 comes after the character 1. Always use-ltand-gtfor numbers.-eqdoes not compare words.[[ "yes" -eq "no" ]]is true: a word that is not a number counts as 0, and 0 equals 0. Always use==for text.
One of two blocks: else
else adds a block that runs when the condition is false. Exactly one of the two blocks runs.
temperature=12
if [[ "$temperature" -ge 20 ]]; then
echo "Warm enough for a picnic."
else
echo "Bring a coat."
fiThis prints Bring a coat..
else block runs. Both paths meet again after fi.More than two paths: elif
elif is short for "else if". It adds another condition, tested only when everything above it was false. Bash goes down the list, runs the block of the first condition that is true, and skips the rest.
score=72
if [[ "$score" -ge 80 ]]; then
echo "Distinction"
elif [[ "$score" -ge 50 ]]; then
echo "Pass"
else
echo "Try again"
fiThis prints Pass. 72 is not at least 80, so the first block is skipped. 72 is at least 50, so the second block runs, and Bash goes straight to fi without looking at the else.
Pass is printed. The else block is never reached.Because the first true condition wins, order matters. If the two conditions were swapped, a score of 95 would print Pass: it is at least 50, and Bash would never reach the test for 80. Put the narrowest condition first.
Combining conditions
Inside [[ ]] three more operators join or reverse conditions:
&&means and: true only when both sides are true.||means or: true when at least one side is true.!means not: it turns true into false and false into true.
age=30
day="Sunday"
if [[ "$age" -ge 18 && "$age" -lt 65 ]]; then
echo "Standard ticket"
fi
if [[ "$day" == "Saturday" || "$day" == "Sunday" ]]; then
echo "Weekend opening hours"
fi
if [[ ! "$day" == "Monday" ]]; then
echo "Open today"
fiThis prints all three lines. 30 is at least 18 and also under 65. The day is Sunday, which makes one side of the || true. And the day is not Monday.
Each side of && and || must be a complete comparison. "Saturday or Sunday" is written with the variable on both sides, as above.
A decision about something the user typed
Decisions become useful when the value comes from the user. This script reads a number and responds to it:
echo "How many tickets?"
read -r tickets
if [[ "$tickets" -gt 10 ]]; then
echo "Sorry, 10 is the most we can sell."
else
echo "That costs $(( tickets * 5 )) pounds."
fiWith 4 typed it prints the question and That costs 20 pounds.. With 12 it prints the question and Sorry, 10 is the most we can sell.. Try both in the Bash playground, with the number in the Standard input box.
Error messages you will meet
A missing space after [[.
age=20
if [[$age -ge 18 ]]; then
echo "You may vote."
fi
echo "Done."solution.sh: line 2: [[20: command not found
Done.You know this message from lesson 1. Without the space, Bash joined [[ and the value 20 into one word, [[20, and looked for a command with that name. Nothing was tested, the block was skipped, and the script carried on.
A missing fi.
age=20
if [[ "$age" -ge 18 ]]; then
echo "You may vote."
echo "Done."solution.sh: line 5: syntax error: unexpected end of file from `if' command on line 2This is a syntax error, as in lesson 1: the text breaks the writing rules of the language, and this site runs none of the script. Here Bash reached the end of the file while still inside an if. The message gives two line numbers. The first, line 5, is where Bash gave up (one past the last line). The second, line 2, is the useful one: the if that was never closed.
A missing then.
age=20
if [[ "$age" -ge 18 ]]
echo "You may vote."
fisolution.sh: line 4: syntax error near unexpected token `fi'
solution.sh: line 4: `fi'"Unexpected token" means Bash met a word it did not expect at that point. It found fi while it was still waiting for then. The mistake is above the line that is named, which is common with syntax errors: when the named line looks correct, read upwards.
What you can do now
You can compare numbers and text, choose between two or more blocks, and combine conditions with and, or and not. In the exercises you will predict which block runs, then write a script that works out a ticket price.
Next: repeat steps with for and while, so that a script can do something many times.