作者:jicanmeng
时间:2016年05月12日
bash shell中,最基本的结构化命令就是if-then语句了。格式如下:
if command then commands fi
也常常用下面的格式:
if command; then commands fi
或者
if command; then commands; fi
如果if后面的command的退出状态码为0(即该命令成功运行),那then后面的commands就会执行。例如:
[jicanmeng@andy tmp]$ if date
> then
> echo "it worked"
> fi
Tue May 10 21:36:36 CST 2016
it worked
[jicanmeng@andy tmp]$
if-then-else语句的格式如下:
if command then commands else commands fi
当然和if-then语句一样,也可以使用简化的形式,只是需要注意在command后面加分号;。举例如下:
[jicanmeng@andy tmp]$ cat 2-if-then-else.sh
#!/bin/bash
testuser=badtest
if grep $testuser /etc/passwd
then
echo The files for user $testuser are:
ls -a /home/$testuser/.b*
else
echo "The user name $testuser does not exist on this system"
fi
[jicanmeng@andy tmp]$ bash 2-if-then-else.sh
The user name badtest does not exist on this system
[jicanmeng@andy tmp]$
有多个条件需要判断的时候,可以用if-then-elif语句。它的格式如下:
if command then commands elif command then commands elif command then commands fi
其实和c语言的语法差不多,这里就不举例子了。
目前为止,你所了解到的if语句中的命令都是普通shell命令。你可能想问,if-then语句是否能测试跟命令的退出状态码无关的条件。
答案是不能。if-then语句运行的唯一标准就是if语句后面接的shell命令是否执行成功。
但在实际的应用中,if语句常常用来比较两个值的大小,例如if a > b
。但是a>b不是shell命令,不满足if-then语句的要求。为了解决这个问题,bash shell引入了test命令。test命令的格式非常简单:
if test condition then commands fi
bash shell提供了另一种在if-then语句中使用test命令的方法:
if [ condition ] then commands fi
这两种形式是完全等价的。这里需要特别注意,你必须在左括号右侧和右括号左侧各加一个空格,否则会报错。
test命令可以用来判断3类条件:1.数值比较; 2.字符串比较; 3.文件比较。下面详细说一说。
使用test命令进行数值比较时,使用如下符号:
符号 | 描述 |
n1 -eq n2 | 检查n1是否等于n2 |
n1 -ne n2 | 检查n1是否不等于n2 |
n1 -ge n2 | 检查n1是否大于等于n2 |
n1 -gt n2 | 检查n1是否大于n2 |
n1 -le n2 | 检查n1是否小于等于n2 |
n1 -lt n2 | 检查n1是否小于n2 |
数值条件测试可以用在数字和变量上。举例如下:
[jicanmeng@andy tmp]$ cat 3-test-number.sh
#!/bin/bash
var1=10
var2=11
if test $var1 -gt 5
then
echo "The test value $var1 is greater than 5"
fi
if [ $var1 -eq $var2 ]
then
echo "The values are equal"
else
echo "The values are different"
fi
[jicanmeng@andy tmp]$ bash 3-test-number.sh
The test value 10 is greater than 5
The values are different
[jicanmeng@andy tmp]$
但是使用test命令测试数值有个限制:不能比较浮点值。必须要记住这一点。例如下面的例子:
[jicanmeng@andy tmp]$ cat 4-test-number-float.sh
#!/bin/bash
var1=`echo "scale=4; 10/3" | bc`
echo "The test value is $var1"
if [ $var1 -gt 3 ]
then
echo "The result is larger than 3"
fi
[jicanmeng@andy tmp]$ bash 4-test-number-float.sh
The test value is 3.3333
test6: line 5: [: 3.3333: integer expression expected
[jicanmeng@andy tmp]$
使用test命令进行字符串比较时,使用如下符号:
符号 | 描述 |
str1 = str2 | 检查str1是否等于str2 |
str1 != str2 | 检查str1是否不等于str2 |
str1 < str2 | 检查str1是否小于str2 |
str1 > str2 | 检查str1是否大于str2 |
-z str1 | 检查str1的长度是否为0 |
-n str1 | 检查str1的长度是否不为0 |
4.2.1 字符串的相等和不相等条件非常简单,例如:
[jicanmeng@andy tmp]$ cat 5-test-string-equal.sh
#!/bin/bash
testuser=jicanmeng
if [ $testuser = $USER ]
then
echo "welcome $testuser"
fi
[jicanmeng@andy tmp]$ bash 5-test-string-equal.sh
welcome jicanmeng
[jicanmeng@andy tmp]$
4.2.2 测试一个字符串是否比另外一个字符串大就繁琐很多。有两个问题需要特别注意:1.大于和小于号必须要转义,否则shell会把它们当作重定向符号而把字符串值当作文件名;2.大于小于的顺序和sort命令所采用的不同。对于第一个例子,有下面的测试程序:
[jicanmeng@andy tmp]$ cat 6-test-string-greater-than-error.sh
#!/bin/bash
#mis-using string comparsions
var1=baseball
var2=hockey
if [ $var1 > $var2 ]
then
echo "$var1 is greater than $var2"
else
echo "$var1 is less than $var2"
fi
[jicanmeng@andy tmp]$ bash 6-test-string-greater-than-error.sh
baseball is greater than hockey
[jicanmeng@andy tmp]$ ls -l hockey
-rw-rw-r--. 1 jicanmeng jicanmeng 0 May 11 23:09 hockey
[jicanmeng@andy tmp]$
可以看到,bash shel将>当作重定向符号,生成了一个hockey文件。程序的执行结果和我们期望的不同。为了解决这个问题,我们需要使用转义字符\。将上面的程序进行修改:
[jicanmeng@andy tmp]$ cat 7-test-string-greater-than-right.sh
#!/bin/bash
#mis-using string comparsions
var1=baseball
var2=hockey
if [ $var1 \> $var2 ]
then
echo "$var1 is greater than $var2"
else
echo "$var1 is less than $var2"
fi
[jicanmeng@andy tmp]$ bash 7-test-string-greater-than-right
baseball is less than hockey
[jicanmeng@andy tmp]$
可以看到,现在的执行结果是正确的。
对于第二个问题,我们也用一个例子来说明:
[jicanmeng@andy tmp]$ cat 8-test-compare-sort.sh
#!/bin/bash
var1=Testing
var2=testing
if [ $var1 \> $var2 ]
then
echo "$var1 is greater than $var2"
else
echo "$var1 is less than $var2"
fi
[jicanmeng@andy tmp]$ bash 8-test-compare-sort.sh
Testing is less than testing
[jicanmeng@andy tmp]$ cat test11
Testing
testing
[jicanmeng@andy tmp]$ sort test11
testing
Testing
[jicanmeng@andy tmp]$
test命令比较两个字符串的大小时,使用标准的ASCII顺序,根据每个字符的ASCII数值来决定派寻顺序。而sort命令使用的是系统的本地化语言设置中定义的排序顺序。对于英语,本地化设置指定了在派寻顺序中小写字母出现在大写字母前面。
注意,test命令使用标准的数学比较符号来进行字符串比较,而用文本代码来进行数值的比较。
4.2.3 -z和-n参数。
还是举例说明:
[jicanmeng@andy tmp]$ cat 9-test-string-n-z.sh
#!/bin/bash
var1=testing
var2=''
if [ -n "$var1" ]
then
echo "The string '$var1' is not empty"
else
echo "The string '$var1' is empty"
fi
if [ -z "$var2" ]
then
echo "The string '$var2' is not empty"
else
echo "The string '$var2' is empty"
fi
if [ -z "$var3" ]
then
echo "The string '$var3' is not empty"
else
echo "The string '$var3' is empty"
fi
[jicanmeng@andy tmp]$ bash 9-test-string-n-z.sh
The string 'testing' is not empty
The string '' is not empty
The string '' is not empty
[jicanmeng@andy tmp]$
变量var3没有被定义,所以$var3返回空,它的长度也是为0的。
要注意的是,用-n和-z测试的时候,后面的变量一定要使用""括起来。原因是因为不加括号有两个缺陷:1. 如果后面的变量为空,那么条件就始终成立。2. 如果后面的变量包括空格,就会提示参数过多的错误。
例如:if [ -n $var1 ]
, 如果以前没有定义var1,那么bash shell直接把本行解释为if [ -n ],这个条件是始终成立的。我们可以看一看手册:
在man test中, -n STRING the length of STRING is nonzero STRING equivalent to -n STRING -z STRING the length of STRING is zero
所以,可以看出来,[ -n ]等价于[ -n -n ],-n作为SRTING,长度不为零,所以本判断条件始终成立。
我们也可以直接在bash shell中测试即可(在没有定义var1变量的情况下):
[ -n $var1 ]; echo $?
[ -z $var1 ]; echo $?
[ -n "$var1" ]; echo $?
[ -z "$var1" ]; echo $?
准确的说,是测试文件的属性,而不是比较文件。例如检查某文件是否存在,是否可读,可写,文件的默认组是否与当前用户相同。下面是常见的选项:
符号 | 描述 |
-e file | 检查file是否存在 |
-d file | 检查file是否存在并是一个目录 |
-f file | 检查file是否存在并是一个文件 |
-r file | 检查file是否存在并可读 |
-w file | 检查file是否存在并可写 |
-x file | 检查file是否存在并可执行 |
-s file | 检查file是否存在并非空 |
-O file | 检查file是否存在并属当前用户所有 |
-G file | 检查file是否存在并且默认组与当前用户相同 |
file1 -nt file2 | 检查file1是否比file2新 |
file1 -lt file2 | 检查file1是否比file2旧 |
这里需要注意的是-G选项,它只会去比较默认组。另外在使用-nt和-ot比较两个文件时,必须先确认文件存在。
if-then语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:
[ condition1 ] && [ condition2 ]
[ condition1 ] || [ condition2 ]
举例如下:
[jicanmeng@andy tmp]$ cat 10-two-command-condition.sh
#!/bin/bash
if [ -d $HOME ] && [ -w $HOME/testing ]
then
echo "The file exists and you can write to it"
else
echo "I can not write to the file"
fi
[jicanmeng@andy tmp]$ bash 10-two-command-condition.sh
I can not write to the file
[jicanmeng@andy tmp]$
bash shell有两项新的扩展,提供了可在if-then语句中使用的高级特性:
在用test命令进行数值比较时,只能进行简单的算数操作,而且还不能用数学符号。双圆括号则提供了非常多的数学符号。其格式如下:
(( expression ))
expression可以是任意的数学赋值表达式或比较表达式。可以使用下面的运算符:
符号 | 描述 |
val++ | 先取值,再加 |
val-- | 先取值,再减 |
++val | 先加,再取值 |
--val | 先减,再取值 |
! | 逻辑求反 |
~ | 位求反 |
** | 幂运算 |
<< | 左位移 |
>> | 右位移 |
& | 位与运算 |
| | 位或运算 |
&& | 逻辑与运算 |
|| | 逻辑或运算 |
你可以在if语句中使用双圆括号命令,也可以在脚本的普通命令中使用来进行赋值。例如:
[jicanmeng@andy tmp]$ cat 11-senoir-square.sh
#!/bin/bash
var1=10
if (( $var1 ** 2 > 90 ))
then
(( var2 = $var1 ** 2 ))
echo "The square of $var1 is $var2"
fi
[jicanmeng@andy tmp]$ bash 11-senoir-square.sh
The square of 10 is 100
[jicanmeng@andy tmp]$
注意:1.不需要将双圆括号中表达式里的大于号转义,这是双圆括号提供的另一个高级特性; 2.双圆括号中赋值时,等号前后可以有空格。
双方括号提供了针对字符串比较的高级特性。其格式如下:
[[ expression ]]
expression使用了test命令中采用的标准字符串进行比较。但它提供了test命令未提供的一个特性:模式匹配。举例如下:
[jicanmeng@andy tmp]$ cat 12-senior-bracket.sh
#!/bin/bash
if [[ $USER == jican* ]]
then
echo "Hello $USER"
else
echo "Sorry, i don't know you"
fi
[jicanmeng@andy tmp]$ bash 12-senior-bracket.sh
Hello jicanmeng
[jicanmeng@andy tmp]$
这里的等于比较,可以用==,也可以用=。
另外,和双圆括号一样,双方括号内大于号也不需要转义。例如:
[jicanmeng@andy tmp]$ cat 13-senior-bracket.sh
#!/bin/bash
#mis-using string comparsions
var1=baseball
var2=hockey
if [[ $var1 > $var2 ]]
then
echo "$var1 is greater than $var2"
else
echo "$var1 is less than $var2"
fi
[jicanmeng@andy tmp]$ bash 13-senior-bracket.sh
baseball is less than hockey
[jicanmeng@andy tmp]$
和c语言一样,bash shell也支持case命令。其格式如下:
case variable in pattern1 | pattern2) commands1;; pattern3) commands2;; *) default commands; esac
举例如下:
[jicanmeng@andy tmp]$ cat 14-case.sh
#!/bin/bash
case $USER in
jicanmeng | barbara)
echo "Welcome, $USER";;
testing)
echo "special testing account";;
*)
echo "Sorry, you are not allowed here";;
esac
[jicanmeng@andy tmp]$ bash 14-case.sh
Welcome, jicanmeng
[jicanmeng@andy tmp]$