|
Softpanorama |
May the source be with you, but remember the KISS principle ;-)
Softpanorama Search
|
| Advanced navigation | Pipes in Loops | |||
| Arithmetic expressions | Comparison operators | Loops in Shell | Shell History | Etc |
Shell cast operators according to the operation used. There are two types of operators available
if [ "$a" -eq "$b" ]
if [ "$a" -ne "$b" ]
if ["$a" -gt "$b" ]
if [ "$a" -ge "$b" ]
if [ "$a" -lt "$b" ]
if [ "$a" -le "$b" ]
(( "$a" < "$b" ))
(( "$a" <= "$b" ))
(( "$a" > "$b" ))
(( "$a" >= "$b" ))
if [ "$a" = "$b" ]
if [ "$a" == "$b" ]
This is a synonym for =.
[[ $a == z* ]] # true if $a starts with an "z" (pattern matching) [[ $a == "z*" ]] # true if $a is equal to z* [ $a == z* ] # file globbing and word splitting take place [ "$a" == "z*" ] # true if $a is equal to z* # Thanks, S.C. |
if [ "$a" != "$b" ]
This operator uses pattern matching within a [[ ... ]] construct.
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
Note that the "<" needs to be escaped within a [ ] construct.
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
Note that the ">" needs to be escaped within a [ ] construct.
See Example 26-4 for an application of this comparison operator.
|
|
The -n test absolutely requires that the string be quoted within the test brackets. Using an unquoted string with ! -z, or even just the unquoted string alone within test brackets (see Example 7-5) normally works, however, this is an unsafe practice. Always quote a tested string. [1] |
For example:
if [ -z "$myvar" ]
then
echo "myvar is not defined"
fi
Sometimes, there are several different ways that a particular comparison
can be made. For example, the following two snippets of code function identically:
if [ "$myvar" -eq 3 ]
then
echo "myvar equals 3"
fi
if [ "$myvar" = "3" ]
then
echo "myvar equals 3"
fi
In the above two comparisons do exactly the same thing, but the first uses arithmetic comparison operators, while the second uses string comparison operators.
Most of the time, while you can omit the use of double quotes surrounding strings and string variables, it's not a good idea. Why? Because your code will work perfectly, unless an environment variable happens to have a space or a tab in it, in which case bash will get confused. Here's an example of a fouled-up comparison:
if [ $myvar = "foo bar oni" ]
then
echo "yes"
fi
In the above example, if myvar equals "foo", the code will work as expected and not print anything. However, if myvar equals "foo bar oni", the code will fail with the following error:
[: too many arguments
In this case, the spaces in "$myvar" (which equals "foo bar oni") end up confusing bash. After bash expands "$myvar", it ends up with the following comparison:
[ foo bar oni = "foo bar oni" ]
Because the environment variable wasn't placed inside double quotes, bash thinks that you stuffed too many arguments in-between the square brackets. You can easily eliminate this problem by surrounding the string arguments with double-quotes. Remember, if you get into the habit of surrounding all string arguments and environment variables with double-quotes, you'll eliminate many similar programming errors. Here's how the "foo bar oni" comparison should have been written:
if [ "$myvar" = "foo bar oni" ]
then
echo "yes"
fi
If you want your environment variables to be expanded, you must enclose them in double quotes, rather than single quotes. Single quotes disable variable (as well as history) expansion.
The above code will work as expected and will not create any unpleasant surprises.Before looking at a second type of looping construct, it's a good idea to become familiar with performing shell arithmetic. Yes, it's true: You can perform simple integer math using shell constructs. Simply enclose the particular arithmetic expression between a "$((" and a "))", and bash will evaluate the expression. Here are some examples:
$ echo $(( 100 / 3 ))
33
$ myvar="56"
$ echo $(( $myvar + 12 ))
68
$ echo $(( $myvar - $myvar ))
0
$ myvar=$(( $myvar + 1 ))
$ echo $myvar
57
logical and
exp1 -a exp2 returns true if both exp1 and exp2 are true.
logical or
exp1 -o exp2 returns true if either exp1 or exp2 are true.
These are similar to the Bash comparison operators && and ||, used within double brackets.
[[ condition1 && condition2 ]] |
if [ "$exp1" -a "$exp2" ] |
Refer to Example 8-2 and Example 26-7 to see compound comparison operators in action.
As S.C. points out, in a compound test, even quoting the string variable might not suffice. [ -n "$string" -o "$a" = "$b" ] may cause an error with some versions of Bash if $string is empty. The safe way is to append an extra character to possibly empty variables, [ "x$string" != x -o "x$a" = "x$b" ] (the "x's" cancel out).
The Advanced Bash Scripting Guide is both a reference and a tutorial on shell scripting. This comprehensive book (the equivalent of 880+ print pages) covers almost every aspect of shell scripting. It contains 340 profusely commented illustrative examples, a number of tables, and a cross-linked index/glossary. Not just a shell scripting tutorial, this book also provides an introduction to basic programming techniques, such as sorting and recursion. It is well suited for either individual study or classroom use. It covers Bash, up to and including version 3.2x.
http://www.tldp.org/LDP/abs/html/
20 Feb 2007
Are you confused by the plethora of testing and comparison options in the Bash shell? This tip helps you demystify the various types of file, arithmetic, and string tests so you will always know when to use{test,[ ],[[ ]],(( )), orif-then-elseconstructs.The Bash shell is available on many Linux® and UNIX® systems today, and is a common default shell on Linux. Bash includes powerful programming capabilities, including extensive functions for testing file types and attributes, as well as the arithmetic and string comparisons available in most programming languages. Understanding the various tests and knowing that the shell can also interpret some operators as shell metacharacters is an important step to becoming a power shell user. This article, excerpted from the developerWorks tutorial LPI exam 102 prep: Shells, scripting, programming, and compiling, shows you how to understand and use the test and comparison operations of the Bash shell.
This tip explains the shell test and comparison functions and shows you how to add programming capability to the shell. You may have already seen simple shell logic using the && and || operators, which allow you to execute a command based on whether the previous command exits normally or with an error. In this tip, you will see how to extend these basic techniques to more complex shell programming.
In any programming language, after you learn how to assign values to variables and pass parameters, you need to test those values and parameters. In shells, the tests set the return status, which is the same thing that other commands do. In fact,
testis a builtin command!The
testbuiltin command returns 0 (True) or 1 (False), depending on the evaluation of an expression, expr. You can also use square brackets:test exprand [ expr ] are equivalent. You can examine the return value by displaying$?; you can use the return value with && and ||; or you can test it using the various conditional constructs that are covered later in this tip.
Listing 1. Some simple tests
[ian@pinguino ~]$ test 3 -gt 4 && echo True || echo false false [ian@pinguino ~]$ [ "abc" != "def" ];echo $? 0 [ian@pinguino ~]$ test -d "$HOME" ;echo $? 0
In the first example in Listing 1, the-gtoperator performs an arithmetic comparison between two literal values. In the second example, the alternate[ ]form compares two strings for inequality. In the final example, the value of the HOME variable is tested to see if it is a directory using the-dunary operator.You can compare arithmetic values using one of
-eq, -ne,-lt, -le, -gt, or -ge, meaning equal, not equal, less than, less than or equal, greater than, and greater than or equal, respectively.You can compare strings for equality, inequality, or whether the first string sorts before or after the second one using the operators
=,!=,<, and>, respectively. The unary operator-ztests for a null string, while-nor no operator at all returns True if a string is not empty.Note: the
<and>operators are also used by the shell for redirection, so you must escape them using\<or\>. Listing 2 shows more examples of string tests. Check that they are as you expect.
Listing 2. Some string tests
[ian@pinguino ~]$ test "abc" = "def" ;echo $? 1 [ian@pinguino ~]$ [ "abc" != "def" ];echo $? 0 [ian@pinguino ~]$ [ "abc" \< "def" ];echo $? 0 [ian@pinguino ~]$ [ "abc" \> "def" ];echo $? 1 [ian@pinguino ~]$ [ "abc" \<"abc" ];echo $? 1 [ian@pinguino ~]$ [ "abc" \> "abc" ];echo $? 1
Some of the more common file tests are shown in Table 1. The result is True if the file tested is a file that exists and that has the specified characteristic.
Table 1. Some common file tests Operator Characteristic -d Directory -e Exists (also -a) -f Regular file -h Symbolic link (also -L) -p Named pipe -r Readable by you -s Not empty -S Socket -w Writable by you -N Has been modified since last being read In addition to the unary tests above, you can compare two files with the binary operators shown in Table 2.
Table 2. Testing pairs of files Operator True if -nt Test if file1 is newer than file 2. The modification date is used for this and the next comparison. -ot Test if file1 is older than file 2. -ef Test if file1 is a hard link to file2. Several other tests allow you to check things such as the permissions of the file. See the man pages for bash for more details or use
help testto see brief information on the test builtin. You can use thehelpcommand for other builtins too.The
-ooperator allows you to test various shell options that may be set usingset -o option, returning True (0) if the option is set and False (1) otherwise, as shown in Listing 3.
Listing 3. Testing shell options
[ian@pinguino ~]$ set +o nounset [ian@pinguino ~]$ [ -o nounset ];echo $? 1 [ian@pinguino ~]$ set -u [ian@pinguino ~]$ test -o nounset; echo $? 0
Finally, the
-aand-ooptions allow you to combine expressions with logical AND and OR, respectively, while the unary!operator inverts the sense of the test. You may use parentheses to group expressions and override the default precedence. Remember that the shell will normally run an expression between parentheses in a subshell, so you will need to escape the parentheses using \( and \) or enclosing these operators in single or double quotes. Listing 4 illustrates the application of de Morgan's laws to an expression.
Listing 4. Combining and grouping tests
[ian@pinguino ~]$ test "a" != "$HOME" -a 3 -ge 4 ; echo $? 1 [ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o 3 -lt 4 \) ]; echo $? 1 [ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o '(' 3 -lt 4 ')' ")" ]; echo $? 1
(( and [[The
testcommand is very powerful, but somewhat unwieldy given its requirement for escaping and given the difference between string and arithmetic comparisons. Fortunately, bash has two other ways of testing that are somewhat more natural for people who are familiar with C, C++, or Java® syntax.The
(( ))compound command evaluates an arithmetic expression and sets the exit status to 1 if the expression evaluates to 0, or to 0 if the expression evaluates to a non-zero value. You do not need to escape operators between((and)). Arithmetic is done on integers. Division by 0 causes an error, but overflow does not. You may perform the usual C language arithmetic, logical, and bitwise operations. Theletcommand can also execute one or more arithmetic expressions. It is usually used to assign values to arithmetic variables.
Listing 5. Assigning and testing arithmetic expressions
[ian@pinguino ~]$ let x=2 y=2**3 z=y*3;echo $? $x $y $z 0 2 8 24 [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w 0 3 8 16 [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w 0 4 8 13
As with
(( )), the[[ ]]compound command allows you to use more natural syntax for filename and string tests. You can combine tests that are allowed for thetestcommand using parentheses and logical operators.
Listing 6. Using the [[ compound
[ian@pinguino ~]$ [[ ( -d "$HOME" ) && ( -w "$HOME" ) ]] && > echo "home is a writable directory" home is a writable directory
The
[[compound can also do pattern matching on strings when the=or!=operators are used. The match behaves as for wildcard globbing as illustrated in Listing 7.
Listing 7. Wildcard tests with [[
[ian@pinguino ~]$ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def c" == a[abc]*\ ?d* ]]; echo $? 1 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $? 1
You can even do arithmetic tests within
[[compounds, but be careful. Unless within a((compound, the<and>operators will compare the operands as strings and test their order in the current collating sequence. Listing 8 illustrates this with some examples.
Listing 8. Including arithmetic tests with [[
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || (( 3 > 2 )) ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 -gt 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 > 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a > 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a -gt 2 ]]; echo $? -bash: a: unbound variable
ConditionalsWhile you could accomplish a huge amount of programming with the above tests and the
&&and||control operators, bash includes the more familiar "if, then, else" and case constructs. After you learn about these, you will learn about looping constructs and your toolbox will really expand.The bash
ifcommand is a compound command that tests the return value of a test or command ($?) and branches based on whether it is True (0) or False (not 0). Although the tests above returned only 0 or 1 values, commands may return other values. Learn more about these in the LPI exam 102 prep: Shells, scripting, programming, and compiling tutorial.The
ifcommand in bash has athenclause containing a list of commands to be executed if the test or command returns 0, one or more optionalelifclauses, each with an additional test andthenclause with an associated list of commands, an optional finalelseclause and list of commands to be executed if neither the original test, nor any of the tests used in theelifclauses was true, and a terminalfito mark the end of the construct.Using what you have learned so far, you could now build a simple calculator to evaluate arithmetic expressions as shown in Listing 9.
Listing 9. Evaluating expressions with if, then, else
[ian@pinguino ~]$ function mycalc () > { > local x > if [ $# -lt 1 ]; then > echo "This function evaluates arithmetic for you if you give it some" > elif (( $* )); then > let x="$*" > echo "$* = $x" > else > echo "$* = 0 or is not an arithmetic expression" > fi > } [ian@pinguino ~]$ mycalc 3 + 4 3 + 4 = 7 [ian@pinguino ~]$ mycalc 3 + 4**3 3 + 4**3 = 67 [ian@pinguino ~]$ mycalc 3 + (4**3 /2) -bash: syntax error near unexpected token `(' [ian@pinguino ~]$ mycalc 3 + "(4**3 /2)" 3 + (4**3 /2) = 35 [ian@pinguino ~]$ mycalc xyz xyz = 0 or is not an arithmetic expression [ian@pinguino ~]$ mycalc xyz + 3 + "(4**3 /2)" + abc xyz + 3 + (4**3 /2) + abc = 35
The calculator makes use of the
localstatement to declare x as a local variable that is available only within the scope of themycalcfunction. Theletfunction has several possible options, as does thedeclarefunction to which it is closely related. Check the man pages for bash, or usehelp letfor more information.As you saw in Listing 9, you need to make sure that your expressions are properly escaped if they use shell metacharacters such as (, ), *, >, and <. Nevertheless, you have quite a handy little calculator for evaluating arithmetic as the shell does it.
You may have noticed the
elseclause and the last two examples in Listing 9. As you can see, it is not an error to passxyzto mycalc, but it evaluates to 0. This function is not smart enough to identify the character values in the final example of use and thus be able to warn the user. You could use a string pattern matching test such as
[[ ! ("$*" == *[a-zA-Z]* ]]
(or the appropriate form for your locale) to eliminate any expression containing alphabetic characters, but that would prevent using hexadecimal notation in your input, since you might use 0x0f to represent 15 using hexadecimal notation. In fact, the shell allows bases up to 64 (usingbase#valuenotation), so you could legitimately use any alphabetic character, plus _ and @ in your input. Octal and hexadecimal use the usual notation of a leading 0 for octal and leading 0x or 0X for hexadecimal. Listing 10 shows some examples.
Listing 10. Calculating with different bases
[ian@pinguino ~]$ mycalc 015 015 = 13 [ian@pinguino ~]$ mycalc 0xff 0xff = 255 [ian@pinguino ~]$ mycalc 29#37 29#37 = 94 [ian@pinguino ~]$ mycalc 64#1az 64#1az = 4771 [ian@pinguino ~]$ mycalc 64#1azA 64#1azA = 305380 [ian@pinguino ~]$ mycalc 64#1azA_@ 64#1azA_@ = 1250840574 [ian@pinguino ~]$ mycalc 64#1az*64**3 + 64#A_@ 64#1az*64**3 + 64#A_@ = 1250840574
Additional laundering of the input is beyond the scope of this tip, so use your calculator with care.
The
elifstatement is very convenient. It helps you in writing scripts by allowing you to simplify the indenting. You may be surprised to see the output of thetypecommand for themycalcfunction as shown in Listing 11.
Listing 11. Type mycalc
[ian@pinguino ~]$ type mycalc mycalc is a function mycalc () { local x; if [ $# -lt 1 ]; then echo "This function evaluates arithmetic for you if you give it some"; else if (( $* )); then let x="$*"; echo "$* = $x"; else echo "$* = 0 or is not an arithmetic expression"; fi; fi }
Of course, you could just do shell arithmetic by using
$(( expression ))with theechocommand as shown in Listing 12. You wouldn't have learned anything about functions or tests that way, but do note that the shell does not interpret metacharacters, such as *, in their normal role when inside(( expression ))or[[ expression ]].
Listing 12. Direct calculation in the shell with echo and $(( ))
[ian@pinguino ~]$ echo $((3 + (4**3 /2))) 35
The "and list" and "or list" constructs provide a means of processing a number of commands consecutively. These can effectively replace complex nested if/then or even case statements. Note that the exit status of an "and list" or an "or list" is the exit status of the last command executed.
1 command-1 && command-2 && command-3 && ... command-n |
Example 3-87. Using an "and list" to test for command-line arguments
1 #!/bin/bash 2 3 # "and list" 4 5 if [ ! -z $1 ] && echo "Argument #1 = $1" && [ ! -z $2 ] && echo "Argument #2 = $2" 6 then 7 echo "At least 2 arguments to script." 8 # All the chained commands return true. 9 else 10 echo "Less than 2 arguments to script." 11 # At least one of the chained commands returns false. 12 fi 13 # Note that "if [ ! -z $1 ]" works, but its supposed equivalent, 14 # "if [ -n $1 ]" does not. This is a bug, not a feature. 15 16 17 # This accomplishes the same thing, coded using "pure" if/then statements. 18 if [ ! -z $1 ] 19 then 20 echo "Argument #1 = $1" 21 fi 22 if [ ! -z $2 ] 23 then 24 echo "Argument #2 = $2" 25 echo "At least 2 arguments to script." 26 else 27 echo "Less than 2 arguments to script." 28 fi 29 # It's longer and less elegant than using an "and list". 30 31 32 exit 0 |
1 command-1 || command-2 || command-3 || ... command-n |
Example 3-88. Using "or lists" in combination with an "and list"
1 #!/bin/bash 2 3 # "Delete", not-so-cunning file deletion utility. 4 # Usage: delete filename 5 6 if [ -z $1 ] 7 then 8 file=nothing 9 else 10 file=$1 11 fi 12 # Fetch file name (or "nothing") for deletion message. 13 14 15 [ ! -f $1 ] && echo "$1 not found. Can't delete a nonexistent file." 16 # AND LIST, to give error message if file not present. 17 18 [ ! -f $1 ] || ( rm -f $1; echo "$file deleted." ) 19 # OR LIST, to delete file if present. 20 # ( command1 ; command2 ) is, in effect, an AND LIST variant. 21 22 # Note logic inversion above. 23 # AND LIST executes on true, OR LIST on false. 24 25 [ ! -z $1 ] || echo "Usage: `basename $0` filename" 26 # OR LIST, to give error message if no command line arg (file name). 27 28 exit 0 |
Clever combinations of "and" and "or" lists are possible, but the logic may easily become convoluted and require extensive debugging.
|
Please visit Heiner Steven SHELLdorado the best shell scripting site on the Internet |
Copyright © 1996-2009 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. Submit comments This document is an industrial compilation designed and created exclusively for educational use and is placed under the copyright of the Open Content License(OPL). Site uses AdSense so you need to be aware of Google privacy policy. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.
Disclaimer:
Last modified: August 15, 2009