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:
1if [ condition 1 ] 2then 3 commands 4elif [ condition 2 ] 5 commands 6else 7 commands 8fi
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#!/bin/bash 2CURRENTDAYOFTHEMONTH=$(date +%d) 3 4if [ $CURRENTDAYOFTHEMONTH -le 10 ] 5then 6 echo "We are within the first 10 days of the month"; 7elif [ $CURRENTDAYOFTHEMONTH -le 20 ] 8then 9 echo "We are within the first 20 days of the month"; 10else 11 echo "We are within the last 10 days of the month"; 12fi
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:
1for variable-name in list 2do 3 Run command on variable-name as $variable-name 4done
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:
1for FILE in file1.txt file2.txt file3.txt 2do 3 chmod 640 $FILE 4done
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.
1for FILE in $(ls -1 | grep file) 2do 3 chmod 640 $FILE 4done
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:
1while condition is true 2do 3 Run commands here 4done
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.
1while read LINE 2do 3 USERNAME=$(echo $LINE | cut -d':' -f 1) 4 USERID=$(echo $LINE | cut -d':' -f 3) 5 echo "The UID of $USERNAME is $USERID" 6done < /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#!/bin/bash 2RANDOMNUM=$(shuf -i1-10 -n1) 3 4NUMBER=0 5 6while [ $NUMBER != $RANDOMNUM ] 7do 8 read -p "Enter a number between 1 and 10: " NUMBER 9done 10echo "Congratulations! Your guess was right!"
The following image shows our game in action: