Escape Character
Meaning
\\
Backslash
\a
Bell (alert)
\b
Backspace
\n
New line
\t
Horizontal tab
\v
Vertical tab
\0
###
ASCII character matching the given octal value
###
\x
HH
ASCII character matching the given hexadecimal value
HH
260
◾
Linux with Operating System Concepts
•
echo –e $FIRST \n$LAST
•
Frank nZappa
•
echo –e "$FIRST \n$LAST"
•
Frank
•
Zappa
•
echo –e "abc\bdef"
•
abdef
− the \b backs up over the ‘c’
•
echo –e "\0106\0162\0141\0156\0153"
•
Frank
•
echo –e "$FIRST\t$LAST"
•
Frank Zappa
But there is a difference as seen with a variable such as
LIST
=
*.txt
. Imagine that
there are three text files in this directory, file1.txt, foo.txt, and someotherfile.txt. Examine
the output of these three commands.
•
echo $LIST
•
file1.txt foo.txt someotherfile.txt
•
echo "$LIST"
•
*.txt
•
echo '$LIST'
•
$LIST
The second and third cases, are what we might expect, the value stored in $LIST and the
literal string $LIST. However, the first example does not return $LIST or the value it stores,
but instead the value $LIST stores is interpreted and we see all of the matching files. The
“” are preventing filename expansion from occurring whereas without the “”, the value of
$LIST (*.txt) is itself being evaluated.
By default, the echo statement will cause a line break at the end of the line’s output. You
can prevent echo from outputting the line break by using the option –n. This is useful when
wanting to prompt the user for input.
7.4.2 Input with read
The input statement is
read
. The read statement is then followed by one or more variables.
If multiple variables are specified they must be separated by spaces. The variable names
Shell Scripting
◾
261
are listed without $ preceding them. The read can be used to input any number of variable
values. The following are some examples of read statements.
•
read FIRST
•
read FIRST LAST
•
read x y z
The second of these examples expects two input values, separated by a space and
third expects three input values, each separated by a space. If the user does not provide
the number of values as expected, any remaining variables are given NULL values. For
instance, if the user were to input 5 10
<
enter
>
in response to the third read statement,
x would be 5, y would be 10, and z would have the value NULL. If the user were to input
more values than needed, the remaining values are grouped together in a list so that the
last variable receives all of these values together. For instance, if the user inputs 5 10 15
20
<
enter
>
for the third read statement, then x is assigned 5, y is assigned 10, and z is
assigned the list of 15 20.
Executing the read statement results in the script pausing while the cursor blinks in the
terminal window. This would not inform the user of the expectations behind the input
statement. Therefore, it is wise to always output a prompting statement that explains what
the user is to do. There are two ways to accomplish this. The first is through an echo state-
ment preceding the input statement. For instance, we might use:
echo Enter your name
read NAME
With this notation, the echo statement’s output would appear and the cursor would then
move to a new line so that the terminal window might look like the following.
Enter your name
Frank
The –n option for echo causes the output to not conclude with a new line. If we modify
the echo statement to use –n, then when we run the program, the prompting message and
the text appear on the same line.
echo –n Enter your name
read NAME
When run, we might see the following after we enter our name.
Enter your nameFrank
262
◾
Linux with Operating System Concepts
Obviously, this does not look very good, so we should probably alter the echo statement
again as follows.
echo –n "Enter your name "
Alternatively, the read statement has an option, -p, that outputs a prompting message
prior to the input. The format is
read –p "message" var1 var2 . . .
The message must be enclosed in quote marks unless the message itself has no blank
spaces. The quote marks are preferred though because you can add blank spaces after it.
For instance:
read –p "Enter your name " NAME
A few other options that you might be interested in are as follows. With –N, you specify
an integer number. The read statement then accepts input as usual but if the input exceeds
the integer number in characters, only that number are used. For instance,
read –N 10
will only read up to the first 10 characters. The –N option will only store the input into one
variable, so you would not use it if your input was for multiple variables.
The –t option allows you to specify a number of seconds. If the input is not received from
keyboard within that number of seconds, the instruction times out and no value is stored
in the variable (or variables if the instruction has multiple variables). A timeout of 0 is used
if you wish to obtain input from a file. In such a case, read will indicate a successful input
by returning the number 0. We examine input from files below.
The -s option has the read statement execute in silent mode. In this mode, any input
characters are not echoed to the terminal window. This can be useful when inputting a
password or other sensitive information. Finally, if you perform read with no variables, the
Bash interpreter uses the environment variable REPLY to store the input in to.
Notice that by default, the read statement accepts input from the keyboard. Recall from
Chapter 2 that one of the forms of redirection is
<
. Through this form of redirection, the
Linux command accepts input from a file rather than keyboard. Since most Linux com-
mands, by default, expect their input to come from disk file, the
<
form of redirection is
not often used. However, if we execute a script and redirect input from a file, then the read
statement accepts input from that file rather than keyboard. Consider the following script,
we will assume is called
input.sh
.
#!/bin/bash
read X
read Y
read Z
echo $((X
+
Y-Z))
Shell Scripting
◾
263
We can run this script in two ways, .
/input.sh
, in which case X, Y, and Z are input
from the keyboard, or .
/input.sh
<
datafile.txt
, in which case X, Y, and Z obtain
the values of the first three rows of data from the file datafile.txt. Any data that follows the
first three rows is unused.
If the input from the user contains multiple data on one line, the script will yield an
error because the value stored in X, Y, or Z is not a single datum. For instance, if the user
were to input
5 10
<
enter
>
15
<
enter
>
20
<
enter
>
then X is storing 5 10, not just a single number. The statement
$((X
+
Y-Z))
then generates
an error. Similarly, if we run the script by redirecting the input from datafile.txt and this
file does not contain exactly one datum per line, we receive the same error. Alternatively,
if datafile.txt has fewer than three rows of data, the remaining variables in the program
are given the value NULL, which will have no impact on the arithmetic operation. For
instance, if datafile.txt has the numbers 10 and 20, the echo statement would output 30
because Z was not given a value.
7.5 SELECTION STATEMENTS
Selection statements are instructions used to make decisions. Based on the evaluation of
a condition, the selection statement selects which instruction(s) to execute. There are four
typical forms of selection statement:
• The if-then statement—if the condition is true, execute the then clause.
• The if-then-else statement—if the condition is true, execute the then clause, other-
wise execute the else clause.
• The if-then-elif-else statement—here, there are many conditions; execute the corre-
sponding clause. If this sounds confusing, it is. We will examine it below.
• The case statement—in some ways, this is a special case of the if-then-elif-else state-
ment. The case statement enumerates value-action pairs and compares a variable
against the list of values. When a match is found, the corresponding action is executed.
In essence, these variations break down into one-way selection, two-way selection, and
n-way selection (for the last two in the list).
7.5.1 Conditions for Strings and Integers
Before we explore the various forms of if-then statements, we need to understand the con-
dition. A condition is a test that compares values and returns either true or false. The com-
parison is typically a variable to a value, a variable to another variable, or a variable to the
264
◾
Linux with Operating System Concepts
value of an expression. For instance, does x equal y? is x greater than 5? does y
+
z equal
a
−
b? In Linux, there are three forms of conditions
• compare two strings together using
=
=
or
!
=
(not equal to),
• compare two integers together using
–eq, -ne, -lt, -gt, -le, -ge
(equal
to, not equal to, less than, greater than, less than or equal to, greater than or equal to
respectively),
• compare a file against an attribute (for instance, is the file readable?).
Notice that string comparisons do not include less than, greater than, and so on.
Conditions can be written using one of two types of syntax. The more common approach
is to use [] marks although the syntax has greater restrictions as we will see. The other
approach uses (()). We first examine the use of [].
For [], the components that make up the condition must all be separated by spaces. To
test the value in the variable NAME to the value Frank, use
[ $NAME
==
Frank ]
.
Without spaces around $NAME,
=
, and Frank, you will receive an error message. In the
case of
[$NAME
==
Frank]
will result in the error
[Frank: command not found]
.
Comparing strings can be tricky. Imagine $NAME above stored “Frank Zappa” (recall, if
a string has a space in it, we must insert it in quote marks). The natural comparison would be
[ $NAME
==
"Frank Zappa" ]
This will also cause an error. In this case, the evaluation of $NAME contains a blank
space causing Bash to misconstrue that $NAME is not a single item. The error message is
somewhat cryptic stating
bash: too many arguments
. The message conveys that
there are too many arguments in the comparison. To enforce Bash to treat the values in
$NAME as a single string, we enclose it in quote marks, as we did the right hand side of the
comparison. Thus, we use “$NAME” instead of $NAME. Our comparison becomes
[ "$NAME"
==
"Frank Zappa" ]
Numeric comparisons are made using the two-letter abbreviations, such as –eq or –lt
(equal to, less than). On either side of the comparison you can have variables, literal values,
or arithmetic expressions. Arithmetic expressions should appear within the proper nota-
tion as described in Section 7.3. Let us assume X, Y, Z, and AGE all store numeric values.
The following are possible comparisons.
•
[ $X –ne $Y ]
•
[ $Z –eq 0 ]
•
[ $AGE –ge 21 ]
•
[ $((X*Y)) –le $((Z+1)) ]
Shell Scripting
◾
265
The alternate syntax for a condition is to use ((condition)) as in
(($x
>
0))
. For this
type of condition, you may omit any dollar sign prior to a variable name and utilize the
relational operators (
>
,
>
=
,
<
,
<
=
,
=
=
, !
=
) in place of the two-letter abbreviated compari-
son operators. You may also omit the blank spaces around each portion of the condition.
For instance, you could replace
[ $x –gt 0 ]
with any of (
($x
>
0))
,
(( $x
>
0 ))
,
((x
>
0))
,
(( x
>
0 )),
or
(( x
>
0 ))
. The earlier examples can be rewritten as follows.
•
((X!=Y))
•
((Z==0))
•
((AGE > 21))
•
((X*Y < Z + 1))
It should be noted that using the relational operators (e.g.,
<
,
>
=
) instead of the two-
lettered versions in conditions with brackets can lead to erroneous behavior. We explore
an example in the next subsection. Therefore, use the two-letter operators when comparing
numeric values in the [] notation and use the relational operators in the (()) notation.
Compound conditions are those that perform multiple comparisons. In order to obtain
a single true/false value from a compound condition, the comparisons are joined together
using one of two Boolean operators, AND or OR (see the appendix if you are unfamiliar
with the use of AND and OR). In Bash programming, AND is denoted as && and OR as
||. For instance,
$X –ne $Y && $Y –eq $Z
is true if the value in X does not equal
the value in Y and the value in Y is equal to the value in Z. To further complicate the syn-
tax, a compound conditional must be placed not in [] but in [[]], that is, double brackets.
Alternatively, a compound conditional can also be placed in (()) symbols. Examples follow.
•
[[ $X –eq 0 && $Y –ne 0 ]] -
true if X is 0 and Y is not 0
•
[[ $X –eq $Y || $X –eq $Z ]] -
true if X equals either Y or Z
•
[[ $X –gt $Y && $X –lt $Z ]] -
true if X falls between Y and Z
•
((X
>
Y || X
<
Y–1))
—true if X is greater than Y or less than Y-1
Many introductory programmers make a mistake with compound conditions in that
they think they can express a condition involving one variable in a shorthand way. For
instance, if we want to test to see if X is equal to both Y and Z, the programmer may define
this as
[[ $X –eq $Y –eq $Z ]]
or
[[ $X –eq $Y && $Z ]]
266
◾
Linux with Operating System Concepts
These are both incorrect. We must specify both
X$ -eq $Y
and
$X –eq $Z
, so the
proper condition is
[[ $X –eq $Y && $X –eq $Z ]]
You can indicate NOT through the exclamation mark. We do not need to use NOT
when comparing strings or numbers because we have “not equal to” available to us in the
form of !
=
and –ne. The use of ! is more useful when performing file comparisons.
Another variation for a condition is to use the
test
command. The test command is
followed by the condition to be tested, but in this case, without either [] or (()) symbols.
If your condition is a compound conditional, then use
–a
and
–o
in place of && and ||
respectively. Here are some examples.
•
test $AGE –ge 21
•
test $AGE –gt 12 –a $AGE –lt 30
•
test –z $AGE
7.5.2 File Conditions
One feature missing from most programming languages that is somewhat unique of Bash’s
scripting language is the ability to test files for properties. This can be extremely useful in
preventing run-time errors. Consider a script that is set up to run other scripts such as
#!/bin/bash
./script1
>
output.txt
./script2
>>
output.txt
./script3
>>
output.txt
If one of the three script files does not exist or is not an actual file (it might be a direc-
tory) or is not executable, then running the script results in a run-time error that might
leave the user wondering what went wrong. By testing file properties, the above script can
be improved. We could improve the above script by testing each file and only executing it
if it exists, is a regular file, and is executable.
The file comparisons use two formats
[
Do'stlaringiz bilan baham: |