When you need to execute different series of commands depending on a condition, the shell provides a standard if/elif/else construct, similar to most programming languages.
The syntax of a basic conditional statement is shown in pseudo-code as follows. Note that there must be a space after the opening bracket and before the closing one:
1 2 3 4 5 6 7 8
if [ condition 1 ] then commands elif [ condition 2 ] commands else commands fi
The elif section is optional and used only when needed to test for a specific alternative to the condition specified through the if.
Let's use the following example to illustrate:
1 2 3 4 5 6 7 8 9 10 11 12
#!/bin/bash CURRENTDAYOFTHEMONTH=$(date +%d) if [ $CURRENTDAYOFTHEMONTH -le 10 ] then echo "We are within the first 10 days of the month"; elif [ $CURRENTDAYOFTHEMONTH -le 20 ] then echo "We are within the first 20 days of the month"; else echo "We are within the last 10 days of the month"; fi
The above script returns different messages depending on the current day of the month. The first if tests whether the current day is less or equal to 10. If this condition evaluates to true, the message We are within the first 10 days of the month is displayed and the other conditions are not tested. Otherwise, elif checks if
$CURRENTDAYOFTHEMONTH is less than or equal to 20. If that is the case, We are within the first 20 days of the month will be returned instead. If none of the previous conditions are met, the default else block will apply, returning We are within the last 10 days of the month.
Once in a while, some tasks will need to be executed repeatedly either a fixed number of times or until a specified condition is satisfied. That is where the for and while looping constructs come in handy. Additionally, at times you will need a script to choose between different courses of action based on the value of a given variable - we'll use the case statement for that.
This looping construct is used to operate on each element of a list of known items. Such a list can be specified explicitly (by listing each element one by one) or as the result of a command. The basic syntax of the for loop is illustrated in the following pseudo-code:
1 2 3 4
for variable-name in list do Run command on variable-name as $variable-name done
variable-name is an arbitrary name that represents an item in list during each iteration.
For example, we can change the permissions on files file1.txt, file2.txt, and file3.txt to 640 using a for loop very easily:
1 2 3 4
for FILE in file1.txt file2.txt file3.txt do chmod 640 $FILE done
In the above example, the variable named FILE is used inside the loop (between the do and done lines) as
$FILE. During the first, second, and third iteration,
$FILE represents file1.txt, file2.txt, and file3.txt, respectively.
An alternative approach is to provide the list of files using the
ls command and
grep to only return the files where the names begin with the word file.
1 2 3 4
for FILE in $(ls -1 | grep file) do chmod 640 $FILE done
This produces the same result as the previous example.
As opposed to the for loop, while is typically used when the number of iterations is not known beforehand or when using for is impractical. Examples include, but are not limited to, reading a file line by line, increasing or decreasing the value of a variable until it reaches a given value, or responding to user input.
The basic syntax of the while loop is:
1 2 3 4
while condition is true do Run commands here done
To illustrate, let's consider two examples. First, let's read the /etc/passwd file line by line and return a message showing each username with its corresponding UID. This is an example of the case when we need to repeat an iteration an undetermined number of times.
The UID, or User ID, is an integer number that identifies each user in the third field in /etc/passwd. It can be returned for each individual account using the id command followed by the username.
1 2 3 4 5 6
while read LINE do USERNAME=$(echo $LINE | cut -d':' -f 1) USERID=$(echo $LINE | cut -d':' -f 3) echo "The UID of $USERNAME is $USERID" done < /etc/passwd
In this example, the condition that is checked at the beginning of each iteration is whether we have reached the end of the file. The variable named LINE represents each line in /etc/passwd. This file is set as the input to the loop by using the < redirection operator. When each line is read, the contents of the first and third fields are stored in USERNAME and USERID, respectively.
Let's take a look at the output of the while loop, which we have saved in a script named users.sh in the current working directory. The content of the output may vary from system to system depending on the number and names of user accounts.
Finally, let's write a simple number-guessing game in a script called guess.sh. When the script is launched, a random number between 1 and 10 is generated and stored in the variable RANDOMNUM. The script then will expect input from the user and indicate if the guess is correct, less than, or greater than the right number. This will continue until the user guesses the number correctly. In this example,
$NUMBER != $RANDOMNUM is enclosed within square brackets to indicate accurately what is the condition to test.
1 2 3 4 5 6 7 8 9 10
#!/bin/bash RANDOMNUM=$(shuf -i1-10 -n1) NUMBER=0 while [ $NUMBER != $RANDOMNUM ] do read -p "Enter a number between 1 and 10: " NUMBER done echo "Congratulations! Your guess was right!"
The following image shows our game in action: