|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Operator | Meaning |
| int1 -eq int2 | Returns True if int1 is equal to int2. |
| int1 -ge int2 | Returns True if int1 is greater than or equal to int2. |
| int1 -gt int2 | Returns True if int1 is greater than int2. |
| int1 -le int2 | Returns True if int1 is less than or equal to int2. |
| int1 -lt int2 | Returns True if int1 is less than int2. |
| int1 -ne int2 | Returns True if int1 is not equal to int2. |
The string operators are used to evaluate string expressions. Table 13.3 lists the string operators that are supported by the three shell programming languages.
| Operator | Meaning |
| str1 = str2 | Returns True if str1 is identical to str2. |
| str1 != str2 | Returns True if str1 is not identical to str2. |
| str | Returns True if str is not null. |
| -n str | Returns True if the length of str is greater than zero. |
| -z str | Returns True if the length of str is equal to zero. |
The test command's file operators are used to perform functions such as checking to see if a file exists and checking to see what kind of file is passed as an argument to the test command. Table 13.4 lists the test command's file operators.
| Operator | Meaning |
| -d filename | Returns True if file, filename is a directory. |
| -f filename | Returns True if file, filename is an ordinary file. |
| -r filename | Returns True if file, filename can be read by the process. |
| -s filename | Returns True if file, filename has a nonzero length. |
| -w filename | Returns True if file, filename can be written by the process. |
| -x filename | Returns True if file, filename is executable. |
The test command's logical operators are used to combine two or more of the integer, string, or file operators or to negate a single integer, string, or file operator. Table 13.5 lists the test command's logical operators.
| Command | Meaning |
| ! expr | Returns True if expr is not true. |
| expr1 -a expr2 | Returns True if expr1 and expr2 are true. |
| expr1 -o expr2 | Returns True if expr1 or expr2 is true. |
The tcsh does not have a test command, but it supports the same function using expressions. The expression operators that tcsh supports are almost identical to those supported by the C language. These expressions are used mostly in the if and while
commands, which are covered later in this chapter in the "Conditional Statements" and "Iteration Statements" sections.
The tcsh expressions support the same kind of operators as the bash and pdksh test command. These are integer, string, file, and logical expressions. The integer operators supported by tcsh expressions are listed in Table 13.6.
| Operator | Meaning |
| int1 <= int2 | Returns True if int1 is less than or equal to int2. |
| int1 >= int2 | Returns True if int1 is greater than or equal to int2. |
| int1 < int2 | Returns True if int1 is less than int2. |
| int1 > int2 | Returns True if int1 is greater than int2. |
The string operators that tcsh expressions support are listed in Table 13.7.
| Operator | Meaning |
| str1 == str2 | Returns True if str1 is equal to str2. |
| str1 != str2 | Returns True if str1 is not equal to str2. |
The file operators that tcsh expressions support are listed in Table 13.8.
| Operator | Meaning |
| -r file | Returns True if file is readable. |
| -w file | Returns True if file is writable. |
| -x file | Returns True if file is executable. |
| -e file | Returns True if file exists. |
| -o file | Returns True if file is owned by the current user. |
| -z file | Returns True if file is of size 0. |
| -f file | Returns True if file is a regular file. |
| -d file | Returns True if file is a directory file. |
The logical operators that tcsh expressions support are listed in Table 13.9.
| Operator | Meaning |
| exp1 || exp2 | Returns True if exp1 is true or if exp2 is true. |
| exp1 && exp2 | Returns True if exp1 is true and exp2 is true. |
| ! exp | Returns True if exp is not true. |
The bash, pdksh, and tcsh each have two forms of conditional statements. These are the if statement and the case statement. These statements are used to execute different parts of your shell program depending on whether certain
conditions are true. As with most statements, the syntax for these statements is slightly different between the different shells.
All three shells support nested if...then...else statements. These statements provide you with a way of performing complicated conditional tests in your shell programs. The syntax of the if statement is the same for bash and pdksh and is shown here:
if [ expression ] then commands elif [ expression2 ] then commands else commands fi
The elif and else clauses are both optional parts of the if statement. Also note that bash and pdksh use the reverse of the statement name in most of their complex statements to signal the end of the statement. In this statement the fi keyword is used to signal the end of the if statement.
The elif statement is an abbreviation of else if. This statement is executed only if none of the expressions associated with the if statement or any elif statements before it were true. The commands associated with the else statement are executed only
if none of the expressions associated with the if statement or any of the elif statements were true.
In tcsh, the if statement has two different forms. The first form provides the same function as the bash and pdksh if statement. This form of if statement has the following syntax:
if (expression1) then commands else if (expression2) then commands else commands endif
Once again, the else if and else parts of the if statement are optional.
The second form of if statement provided by tcsh is a simple version of the first if statement. This form of if statement evaluates only a single expression. If the expression is true, it executes a single command; if the expression is false, nothing
happens. The syntax for this form of if statement is the following:
if (expression) command
This statement could be written using the first form of if statement by writing the if without any else or else if clauses. This form just saves a little typing.
The following is an example of a bash or pdksh if statement. This statement checks to see if there is a .profile file in the current directory:
if [ -f .profile ] then echo "There is a .profile file in the current directory." else echo "Could not find the .profile file." fi
The same statement written using the tcsh syntax is shown here:
#
if ( { -f .profile } ) then
echo "There is a .profile file in the current directory."
else
echo "Could not find the .profile file."
endif
Notice that in the tcsh example the first line starts with a #. This is required for tcsh to recognize the file containing the commands as a tcsh script file.
The case statement enables you to compare a pattern with several other patterns and execute a block of code if a match is found. The shell case statement is quite a bit more powerful than the case statement in Pascal or the switch statement in C. This
is because in the shell case statement you can compare strings with wildcard characters in them, whereas with the Pascal and C equivalents you can compare only enumerated types or integer values.
Once again, the syntax for the case statement is identical for bash and pdksh and different for tcsh. The syntax for bash and pdksh is the following:
case string1 in str1) commands;; str2) commands;; *) commands;; esac
string1 is compared to str1 and str2. If one of these strings matches string1, the commands up until the double semicolon (;;) are executed. If neither str1 nor str2 matches string1, the commands associated with the asterisk are executed. This is the
default case condition because the asterisk matches all strings.
The tcsh equivalent of the bash and pdksh case statement is called the switch statement. This statement's syntax closely follows the C switch statement syntax. Here it is:
switch (string1) case str1: statements breaksw case str2: statements breaksw default: statements breaksw endsw
This behaves in the same manner as the bash and pdksh case statement. Each string following the keyword case is compared with string1. If any of these strings matches string1, the code following it up until the breaksw keyword is executed. If none of
the strings matches, the code following the default keyword up until the breaksw keyword is executed.
The following code is an example of a bash or pdksh case statement. This code checks to see if the first command-line option was -i or -e. If it was -i, the program counts the number of lines in the file specified by the second command-line option that
begins with the letter i. If the first option was -e, the program counts the number of lines in the file specified by the second command-line option that begins with the letter e. If the first command-line option was not -i or -e, the program prints a
brief error message to the screen.
case $1 in -i) count='grep ^i $2 | wc -l' echo "The number of lines in $2 that start with an i is $count" ;; -e) count='grep ^e $2 | wc -l' echo "The number of lines in $2 that start with an e is $count" ;; * ) echo "That option is not recognized" ;; esac
The same example written in tcsh syntax is shown here:
# remember that the first line must start with a # when using tcsh switch ( $1 ) case -i | i: set count = 'grep ^i $2 | wc -l' echo "The number of lines in $2 that begin with i is $count" breaksw case -e | e: set count = 'grep ^e $2 | wc -l' echo "The number of lines in $2 that begin with e is $count" breaksw default: echo "That option is not recognized" breaksw endsw
The shell languages also provide several iteration or looping statements. The most commonly used of these is the for statement.
The for statement executes the commands that are contained within it a specified number of times. bash and pdksh have two variations of the for statement.
The for statement syntax is the same in both bash and pdksh.
The first form of the for statement that bash and pdksh support has the following syntax:
for var1 in list do commands done
In this form, the for statement executes once for each item in the list. This list can be a variable that contains several words separated by spaces, or it can be a list of values that is typed directly into the statement. Each time through the loop,
the variable var1 is assigned the current item in the list, until the last one is reached.
The second form of for statement has the following syntax:
for var1 do statements done
In this form, the for statement executes once for each item in the variable var1. When this syntax of the for statement is used, the shell program assumes that the var1 variable contains all the positional parameters that were passed in to the shell
program on the command line.
Typically this form of for statement is the equivalent of writing the following for statement:
for var1 in "$@" do statements done
The equivalent of the for statement in tcsh is called the foreach statement. It behaves in the same manner as the bash and pdksh for statement. The syntax of the foreach statement is the following:
foreach name (list) commands end
The following is an example of the bash or pdksh style of for statement. This example takes as command-line options any number of text files. The program reads in each of these files, converts all the letters to uppercase, and then stores the results in
a file of the same name but with a .caps extension.
for file do tr a-z A-Z < $file >$file.caps done
The same example written in tcsh shell language is shown next:
# foreach file ($*) tr a-z A-Z < $file >$file.caps end
Another iteration statement offered by the shell programming language is the while statement. This statement causes a block of code to be executed while a provided conditional expression is true. The syntax for the while statement in bash and pdksh is
the following:
while expression do statements done
The syntax for the while statement in tcsh is the following:
while (expression) statements end
The following is an example of the bash and pdksh style of while statement. This program lists the parameters that were passed to the program, along with the parameter number.
count=1 while [ -n "$*" ] do echo "This is parameter number $count $1" shift count='expr $count + 1' done
As you will see in the section titled "The shift Command," the shift command moves the command-line parameters over one to the left.
The same program written in the tcsh language is shown next:
# set count = 1 while ( "$*" != "" ) echo "This is parameter number $count $1" shift set count = 'expr $count + 1' end
The until statement is very similar in syntax and function to the while statement. The only real difference between the two is that the until statement executes its code block while its conditional expression is false, and the while statement executes
its code block while its conditional expression is true. The syntax for the until statement in bash and pdksh is
until expression do commands done
The same example that was used for the while statement can be used for the until statement. All you have to do to make it work is negate the condition. This is shown in the following code:
count=1 until [ -z "$*" ] do echo "This is parameter number $count $1" shift count='expr $count + 1' done
The only difference between this example and the while statement example is that the -n test command option (which means that the string has nonzero length) was removed, and the -z test option (which means that the string has zero length) was put in its
place.
In practice the until statement is not very useful, because any until statement you write can also be written as a while statement.
tcsh does not have an equivalent of the until statement other than rewriting it as a while loop.
bash, pdksh, and tcsh all support a command called shift. The shift command moves the current values stored in the positional parameters to the left one position. For example, if the values of the current positional parameters are
$1 = -r $2 = file1 $3 = file2
and you executed the shift command
shift
the resulting positional parameters would be as follows:
$1 = file1 $2 = file2
You can also move the positional parameters over more than one place by specifying a number with the shift command. The following command would shift the positional parameters two places:
shift 2
This is a very useful command when you have a shell program that needs to parse command-line options. This is true because options are typically preceded by a hyphen and a letter that indicates what the option is to be used for. Because options are
usually processed in a loop of some kind, you often want to skip to the next positional parameter once you have identified which option should be coming next. For example, the following shell program expects two command-line optionsone that specifies
an input file and one that specifies an output file. The program reads the input file, translates all the characters in the input file into uppercase, then stores the results in the specified output file.
The following example was written using bash, pdksh syntax.
while [ "$1" ] do if [ "$1" = "-i" ] then infile="$2" shift 2 elif [ "$1" = "-o" ] then outfile="$2" shift 2 else echo "Program $0 does not recognize option $1" fi done tr a-z A-Z <$infile >$outfile
pdksh offers one iteration statement that neither bash nor tcsh provides. This is the select statement. This is actually a very useful statement. It is quite a bit different from the other iteration statements because it actually does not execute a
block of shell code repeatedly while a condition is true or false. What the select statement does is enable you to automatically generate simple text menus. The syntax for the select statement is
select menuitem [in list_of_items] do commands done
where square brackets are used to enclose the optional part of the statement.
When a select statement is executed, pdksh creates a numbered menu item for each element in the list_of_items. This list_of_items can be a variable that contains more than one item, such as choice1 choice2, or it can be a list of choices typed in the
command. For example:
select menuitem in choice1 choice2 choice3
If the list_of_items is not provided, the select statement uses the positional parameters just as with the for statement.
Once the user of the program containing a select statement picks one of the menu items by typing the number associated with it, the select statement stores the value of the selected item in the menuitem variable. The statements contained in the do block
can then perform actions on this menu item.
The following example illustrates a potential use for the select statement. This example displays three menu items, and when the user chooses one of them it asks whether that was the intended selection. If the user enters anything other than y or Y, the
menu is redisplayed.
select menuitem in pick1 pick2 pick3 do echo "Are you sure you want to pick $menuitem" read res if [ $res = "y" -o $res = "Y" ] then break fi done
A few new commands are introduced in this example. The read command is used to get input from the user. It stores anything that the user types into the specified variable. The break command is used to exit a while, until, repeat, select, or for
statement.
tcsh has an iteration statement that has no equivalent in pdksh or bash. This is the repeat statement. The repeat statement executes a single command a specified number of times. The syntax for the repeat statement is the following:
repeat count command
The following is an example of the repeat statement. It takes a set of numbers as command-line options and prints that number of periods to the screen. This program acts as a very primitive graphing program.
# foreach num ($*) repeat $num echo -n "." echo "" end
Any repeat statement can be rewritten as a while or for statement. The repeat syntax is simply more convenient.
The shell languages enable you to define your own functions. These functions behave in much the same way as functions you define in C or other programming languages. The main advantage of using functions as opposed to writing all of your shell code in
line is for organizational purposes. Code written using functions tends to be much easier to read and maintain and also tends to be smaller, because you can group common code into functions instead of putting it everywhere it is needed.
The syntax for creating a function in bash and pdksh is the following:
fname () {
shell commands
}
pdksh also allows the following syntax:
function fname {
shell commands
}
Both of these forms behave in the exact same way.
Once you have defined your function using one of these forms, you can invoke it by entering the following command:
fname [parm1 parm2 parm3 ...]
The tcsh shell does not support functions.
Notice that you can pass any number of parameters to your function. When you do pass parameters to a function, it sees those parameters as positional parameters, just as a shell program does when you pass it parameters on the command line. For example,
the following shell program contains several functions, each of which is performing a task associated with one of the command-line options. This example illustrates many of the topics covered in this chapter. It reads all the files that are passed on the
command line anddepending on the option that was usedwrites the files out in all uppercase letters, writes the files out in all lowercase letters, or prints the files.
upper () {
shift
for i
do
tr a-z A-Z <$1 >$1.out
rm $1
mv $1.out $1
shift
done; }
lower () {
shift
for i
do
tr A-Z a-z <$1 >$1.out
rm $1
mv $1.out $1
shift
done; }
print () {
shift
for i
do
lpr $1
shift
done; }
usage_error () {
echo "$1 syntax is $1 <option> <input files>"
echo ""
echo "where option is one of the following"
echo "p to print frame files"
echo "u to save as uppercase"
echo "l to save as lowercase"; }
case $1
in
p | -p) print $@;;
u | -u) upper $@;;
l | -l) lower $@;;
*) usage_error $0;;
esac
This chapter introduced you to many of the features of the bash, pdksh, and tcsh programming languages. As you become familiar with using Linux, you will find that you use shell programming languages more and more often.
Even though the shell languages are very powerful and also quite easy to learn, you might run into some situations where shell programs are not suited to the problem you are solving. In these cases you may want to investigate the possibility of using one of the other languages available under Linux. Some of your options are C and C++, which are described in Chapters 27, "Programming in C" and 28, "Programming in C++;" gawk, which is described in Chapter 26, "gawk;" and Perl, which is described in Chapter 29, "Perl."