Chapter Syllabus
11.1 Arithmetic and Logic Operations
11.2 The while-do-done Loop
11.3 The until-do-done Loop
11.4 The for-do-done Loop
11.5 Breaking a Loop
11.6 Text Processing
Loops are used to perform an operation repeatedly until a condition becomes true or false. The test or let command is used to check the condition every time a repetition is made. All loop structures used in shell programming start with a keyword. The block of commands that is executed repeatedly is enclosed by the do-done keywords.
There are three basic types of loops. The first one is the for-do-done loop, which is used to execute a block of commands for a fixed number of times. The while-do-done loop checks for a condition and goes on executing a block of commands until that condition becomes false. The until-do-done loop repeats the execution of a block of commands until a condition becomes true. As soon as the condition becomes true, the loop terminates.
All of these loops are controlled by a variable known as the control variable. This variable gets a new value on every repetition of the loop. The let command is also used to make arithmetic, logic, and assignment operations inside the loops and to change the value of the control variable.
In this chapter, we will start with arithmetic and logic operations performed with the let command. The three loops will be discussed one-by-one. You will find
the general syntax of each loop as well as a flow diagram. In the end, you will find some text processing examples and their use in loops.
11.1 Arithmetic and Logic Operations
The let command performs both arithmetic and logic operations. The use of the let command is important because all loops depend on the control variable. The value of this control must be changed during the execution of the loop. Usually this value is incremented or decremented with the help of the let command. The loop structures also need logic operations, used for the testing value of the control variable. This is the second use of the let command. Like the test command, the let command also has explicit and implicit modes.
Explicit Mode let Command
In the explicit mode, the word let is used in the command line. Consider the following example of the use of the command.
$ A=5
$ B=3
$ let "C=A+B"
$ echo $C
8
$
You created two new shell variables A and B and assigned these variables numeric values. Then you used the let command to sum these two values and assign the result to a third variable C. To display the value of this variable, you used the echo command. Like this arithmetic operation, you can also perform logic operations with the let command as shown in the following example.
$ var1=5
$ var2=3
$ let "var1
$ echo $?
1
$ let "var1>var2"
$ echo $?
0
$
In this example, you compared two variables. The first comparison was not true, so the result code returned is 1. The second comparison is true and the result code is zero.
Implicit Mode let Command
You can replace the word let with double parentheses on each side of the expression. The above example, where you added two variables, can also be accomplished as follows.
$ A=5
$ B=3
$ ((C=A+B))
$ echo $C
8
$
The let command can also perform complex operations like the one shown here.
((A=A+(3*B)/(A-1)))
While evaluating the result of an expression, the usual arithmetic rules are applied. Parentheses can be used to alter the order of evaluation.
Table 11-1 lists the operators that can be used with the let command.
The first two operators are unary operators that need only one operand. All other operators are binary operators and need two operands. You will find many examples of the use of the let command in this chapter.
Table 11-1. Operators Used with the let Command
Operator
Description
-
Unary minus
!
Unary negation (same value but with a negative sign)
=
Assignment
+
Addition
-
Subtraction
*
Multiplication
/
Integer division
%
Remainder
Table 11-1. Operators Used with the let Command
Operator Description
<
Less than
>
Greater than
<=
Less than or equal to
>=
Greater than or equal to
==
Comparison for equality
!=
Comparison for nonequality
11.2 The while-do-done Loop
The while-do-done loop is used to test a condition before the execution of the block of commands contained inside the loop. The command block is executed if the test is successful and returns a true value. It may happen that the command block never executes if the test fails the very first time. The loop continues to execute as long as the condition remains true. The general syntax of the while-do-done loop is shown here.
while condition
do
command block
done
The condition is usually an expression containing a test or let command. Both of these commands are usually used in implicit mode. The while-do-done loop can be represented as a flow diagram as shown in Figure 11-1.
Figure 11-1. The while-do-done loop.
Let us see an example of the loop. We start with assigning the value 1 to a variable VAR1. Every time the loop executes, we double the value of the variable. The loop continues as long as the value of the variable is less than 100. As soon as the variable value reaches this limit, the loop execution terminates, and the next command after the done keyword is executed. The shell program script-20 follows.
#!/usr/bin/sh
echo "The while loop example"
echo
VAR1=1
while ((VAR1 < 100))
do
echo "Value of the variable is : $VAR1"
((VAR1 = VAR1 * 2))
done
echo
echo "The loop execution is finished"
You can also use the test command instead of the let command in the comparison made in the while condition. In that case, this line will be:
while [ VAR1 -lt 100 ]
When you execute this program, you will see the output shown here.
$ ./script-20
The while loop example
Value of the variable is : 1
Value of the variable is : 2
Value of the variable is : 4
Value of the variable is : 8
Value of the variable is : 16
Value of the variable is : 32
Value of the variable is : 64
The loop execution is finished
$
A while loop may become an infinite loop if you make a mistake while making a test decision. For example, consider the following program where you start with a value of VAR1 equal to 1. You add 2 to the value of VAR1 at each step. You compare the value of the variable with 10. This condition is never fulfilled because the value of the variable never becomes 10. It goes from 9 to 11, skipping the value to which the comparison is made. By changing "!=" to "<=", you can solve the problem. The program script-21 is shown here.
#!/usr/bin/sh
echo "The while loop example"
echo
VAR1=1
while ((VAR1 != 10))
do
echo "Value of the variable is : $VAR1"
((VAR1 = VAR1 + 2))
done
echo
echo "The loop execution is finished"
Another example of an infinite loop is when you forget to modify the control variable inside the loop, such as in the code segment that follows.
VAR1=1
while ((VAR1 != 10))
do
echo "Value of the variable is : $VAR1"
done
Here the value of VAR1 is always 1, and the condition remains true, resulting in an infinite loop.
11.3 The until-do-done Loop
The until-do-done loop is like the while-do-done loop. The only difference is that it tests the condition and goes on executing as long as the condition remains false. It terminates execution as soon as the condition becomes true. The general syntax of this loop is:
until condition
do
command block
done
The flow diagram of the until-do-done loop is shown in Figure 11-2.
Figure 11-2. The until-do-done loop.
As you may have noticed, the only difference between Figure 11-1 and Figure 11-2 is that the "True" and "False" positions have been interchanged. Here is script-22, which has the same result as script-20 but was implemented using an until-do-done loop.
#!/usr/bin/sh
echo "The until loop example"
echo
VAR1=1
until (( VAR1 > 100 ))
do
echo "Value of the variable is : $VAR1"
((VAR1 = VAR1 * 2))
done
echo
echo "The loop execution is finished"
11.4 The for-do-done Loop
The for-do-done loop is executed on a list of elements. The list of elements is assigned to a variable one-by-one. The value of this variable is processed inside the loop. The loop continues to execute until all of the list elements are processed and there are no more elements in the list. The general syntax of the for-do-done loop is:
for var in list
do
command block
done
The for-do-done loop flow diagram is shown in Figure 11-3.
Figure 11-3. The for-do-done loop.
As an example of the use of this loop, if you want to list all executable files in your home directory, you can use the following program (script-23) for this purpose.
#!/usr/bin/sh
echo "List of all executable files in home directory"
cd $HOME
for F in *
do
if [ -x $F ]
then
ll $F
fi
done
The asterisk character represents all files in this directory. When you run this program, the result is shown as follows. You may have a different result on your system. There may be other uses of this program. You can utilize this script to find all files that have the SUID bit set or some other type of file with slight modifications.
$ ./script-23
List of all executable files in home directory
-rwxr-xr-x 1 boota users 267 Oct 18 19:23 script-00
-rwxr-xr-x 1 boota users 131 Oct 18 19:53 script-01
-rwxr-xr-x 1 boota users 198 Oct 18 20:01 script-02
-rwxr-xr-x 1 boota users 100 Oct 18 20:07 script-03
-rwxr-xr-x 1 boota users 121 Oct 18 20:16 script-04
-rwxr-xr-x 1 boota users 132 Oct 18 21:25 script-05
-rwxr-xr-x 1 boota users 232 Oct 18 23:11 script-06
-rwxr-xr-x 1 boota users 177 Oct 18 22:04 script-07
-rwxr-xr-x 1 boota users 142 Oct 19 17:43 script-08
-rwxr-xr-x 1 boota users 170 Oct 19 18:04 script-09
-rwxr-xr-x 1 boota users 638 Oct 19 18:30 script-10
-rwxr-xr-x 1 boota users 313 Oct 19 19:31 script-11
-rwxr-xr-x 1 boota users 195 Oct 20 23:16 script-20
-rwxr-xr-x 1 boota users 193 Oct 20 23:00 script-21
-rwxr-xr-x 1 boota users 195 Oct 21 17:04 script-22
-rwxr-xr-x 1 boota users 140 Oct 21 17:07 script-23
$
The script-24 is another example of the for-do-done loop, where a list is provided to the for command. This list contains the names of weekdays, which the program reads one-by-one and prints them on your terminal screen.
#!/usr/bin/sh
for DAY in Sunday Monday Tuesday Wednesday Thursday Friday
Saturday
do
echo "The day is : $DAY"
done
The result of this program is:
$ ./script-24
The day is : Sunday
The day is : Monday
The day is : Tuesday
The day is : Wednesday
The day is : Thursday
The day is : Friday
The day is : Saturday
$
Changing File Access Date and Time
Let's suppose you want to change the access time of all files in your current directory to the current time. You can use the touch command with a small shell script as shown here.
for FILE in *
do
touch $FILE
done
Accessing Command Line Parameters
To process all command line parameters one-by-one using a for-do-done loop, the following code segment may be used.
for ARG in $*
do
echo $ARG
done
You can replace the echo command with any command or a block of commands to get a desired result.
Study Break
Use of Shell Loops
All of the three shell loops have their own applications. However, the while-do-done and until-do-done loops can be used interchangeably in many cases. Let's have some practice with these loops. Using a while-do-done loop, write a shell program that takes a number as input and then prints its table from 1 to 10. Now change the while-do-done
loop to an until-do-done loop to get the same functionality. Use the for-do-done loop and pass a list of numbers from 1 to 10 to the for statement. Again print the table with this arrangement.
11.5 Breaking a Loop
There may be situations when you want to break or discontinue the execution of commands inside the command block of a loop. This is done when a particular condition is met and you don't want any further execution of commands in the command block. You may also need to check an error condition and discontinue execution of the program depending on that error condition.
The shell provides three mechanisms for breaking the normal execution of loops. These are break, continue, and exit.
The break Command
The break command discontinues the execution of loop immediately and transfers control to the command following the done keyword. You can pass a number n as an argument to the break command. In that case, the break command jumps to the command after nth occurrence of the done keyword. Consider script-25 shown here. It asks you to enter a file name. If the file is a regular file, it uses the cat command and displays its contents. If the file is not a regular file, it displays a message and then quits execution after displaying the message "Good bye." The program uses an infinite loop and will not terminate until you enter a nonregular file name, such as a directory name.
#!/usr/bin/sh
while true
do
echo "Enter name of file to be displayed: \c"
read FILE
if [ ! -f $FILE ]
then
echo "This is not a regular file"
break
fi
cat $FILE
done
echo "Good bye"
Let us execute and enter a nonregular file such as /etc.
$ ./script-25
Enter name of file to be displayed: /etc
This is not a regular file
Good bye
$
The continue Command
The continue command is slightly different from the break command. When encountered, it skips the remaining part of the loop and transfers the control to the start of the loop for the next iteration. The script-26 does the same job as script-25 but with the use of the continue command. In this script, we have changed the test condition, and the loop goes on executing until you enter a file name that is not a regular file. At this point, the loop breaks and the command after the loop is executed.
#!/usr/bin/sh
while true
do
echo "Enter name of file to be displayed: \c"
read FILE
if [ -f $FILE ]
then
cat $FILE
continue
fi
echo "This is not a regular file"
break
done
echo "Good bye"
The exit Command
The exit command completely terminates the program. It returns an exit code that is optionally provided as its argument in the program. If the exit command doesn't have any arguments, it returns the exit code of the command executed just before it. This command is used when a critical error is encountered, and further execution of the program may cause faulty results. For example, dividing a number by zero is illegal, so you want to check this condition before a
command is executed that divides a number by zero. Program script-27 reads a number entered by the user and then divides 100 by this number. It then displays the quotient and the remainder. If you try to divide by zero, the program displays an error message and terminates immediately.
#!/usr/bin/sh
NUM=100
while true
do
echo "Enter a divisor for integer 100 : \c"
read DIV
if [ $DIV -eq 0 ]
then
echo "Divide by zero is not permitted"
exit 1
fi
(( QUO = NUM / DIV ))
(( REM = NUM % DIV ))
echo "The quotient is : $QUO"
echo "The remainder is : $REM"
done
11.6 Text Processing
You have used the grep command as a filter to extract or delete lines containing a particular text pattern. Here you will learn two more commands that are useful for text processing. The sed command is a stream editor that takes text from stdin and sends it to stdout after editing it. The cut command is used to extract a desired part of text from a line. It also takes its input from stdin and sends its output to stdout.
Using sed
The stream editor is a useful tool to edit large amounts of text at one time. For example, you may need to search for a word in a large file and replace it with another word. Let's try to replace the word "echo" with "ECHO" in script-27. The sed command will do the job as follows.
$ sed s/echo/ECHO/g script-27
#!/usr/bin/sh
NUM=100
while true
do
ECHO "Enter a divisor for integer 100 : \c"
read DIV
if [ $DIV -eq 0 ]
then
ECHO "Divide by zero is not permitted"
exit 1
fi
(( QUO = NUM / DIV ))
(( REM = NUM % DIV ))
ECHO "The quotient is : $QUO"
ECHO "The remainder is : $REM"
done
If you want to do an operation on all files, you can write a shell program to accomplish the job. Program script-28 shown here replaces "echo" with "ECHO" in all files of the current directory.
#!/usr/bin/sh
for FILE in *
do
cat $FILE |sed s/echo/ECHO/g >tempfile
cp tempfile $FILE
done
rm tempfile
As you can see, this is a very useful tool to make changes to a large number of files that could take a long time otherwise. Consider you are writing a book and want to change "figure" to "Fig" in all chapters. If you don't know how to do it in an efficient way, you may start editing all files manually, spending hours on a job that need take only a few minutes.
There are many ways to use sed that make it a very useful tool. For additional information, consult the sed manual pages.
Using cut
The cut command is used to extract a particular part of data from a line of text. If the data are in the form of fields, you can extract particular fields. For example, if you want to list all user names on your system, you can use the cut command on the /etc/passwd file as follows:
cut -f 1 -d : /etc/passwd
or
cat /etc/passwd | cut -f 1 -d :
Here the -f 1 option tells the command that you want to extract field number 1. The -d : option shows that the fields in the data are separated by a delimiter
colon ":". Since user names are in the start of each line in /etc/passwd and they are followed by a colon, the command extracts all user names from the file.
You may also use the cut command to extract a particular number of characters from a file. To extract the first eight characters from every line in the /etc/passwd file, you may use the following command.
cat /etc/passwd | cut -c 1-8
Here you specified a range of characters using the -c 1-8 option. See the manual pages for more information on the cut command.
Let us use the cut command in a shell program script-29. This script is used to send an email message to all users on a system. The message contents are stored in a file mailfile. You use the mailx command to send the message.
#!/usr/bin/sh
for USER in $(cut -f 1 -d : /etc/passwd)
do
mailx -s "Test mail" $USER
done
You have used the cut command to create a list of user names and then send a mail message to each name in the list.
The sleep Command
The sleep command is used to suspend execution for a certain amount of time. You provide the number of seconds as the argument to the sleep command. The following code segment lists all files in the current directory with a pause of five seconds between every file.
for FILE in *
do
ll $FILE
sleep 5
done
Test Your Knowledge
1:
Which command will you use to add the values of two variables VAR1 and VAR2, and store the result in VAR3?
A. [ $VAR3 = $VAR1 + $VAR2 ]
B. $VAR3 = [ $VAR1 + $VAR2 ]
C. $VAR3 = (( VAR1 + VAR2 ))
D. $VAR3 = VAR1 + VAR2 ))
2:
You want to wait for 10 seconds at the end of the loop in each loop cycle. Which command will you use?
A. sleep
B. pause
C. wait
D. Any of the three commands can be used.
3:
Consider the following code segment. How many times does the loop execute?
A=1
until [ $A < 10 ]
do
echo $A
(( $A=$A+1))
done
A. zero
B. one
C. nine
D. ten
4:
What will be the output of the program shown here?
#!/usr/bin/sh
A=1
while [ $A -lt 10 ]
do
B=1
while [ $B -lt 10 ]
do
break 2
echo "Inner loop"
done
echo "Outer Loop"
done
A. "Inner Loop" will be printed 10 times.
B. "Outer Loop" will be printed 10 times.
C. "Outer Loop" will be printed 9 times.
D. Nothing will be printed.
5:
While writing a program, you meet a situation where you want to break the normal execution and shift control to the beginning of the loop, skipping the remaining commands in the loop. Which command will you use?
A. break
B. continue
C. exit
D. shift
Knowledge question 1 is faulty (or a trick question) as none of the four given options is correct. To assign to VAR3, you would use
ReplyDeleteVAR3 = (( VAR1 + VAR2 ))
but not
$VAR3 = (( VAR1 + VAR2 ))
The variable name on the left of "=" does not need a leading $ character (or would be replaced by its value before assignment).
Knowledge question 4 is a bad example as neither of the loops increments the loop variable, making them infinite loops. Without the break command, the script would never terminate, ruling out answers A-C in the first place.
ReplyDelete