第二章 使用结构化命令
2.1 if-else语句
最基本的结构化命令就是if-then
语句,其中then
部分,可以使用多条命令。
if command
then command
fi
//或者使用下面格式
if command; then
command
fi
其他编程语言中,if语句之后的对象是一个等式,这个等式的求值结果是TRUE
或者FALSE
,但是bash shell的if会运行if
后面的命令,如果该命令的退出状态码是0(命令运行成功),位于then部分的命令就会被执行。如果该命令的退出码是其他值,then部分的命令就不会执行,bash shell会继续执行脚本中的下一个命令。fi
语句用来标识if-then
语句到此结束。
#此脚本在if行采用了pwd命令,如果命令成功结束,echo语句就会显示该文本字符串。
$ cat test1.sh
#!/bin/bash
# testing the if statement
if pwd
then
echo "It worked"
fi
由于IanNotaCommand
是个错误的命令,所以会产生一个非零的退出的状态码,且bash shell会跳过then
语句的echo
语句。
$ cat test2.sh
#!/bin/bash
# testing a bad command
if IamNotaCommand
then
echo "It worked"
fi
echo "We are outside the if statement"
$
$ ./test2.sh
./test2.sh: line 3: IamNotaCommand: command not found
We are outside the if statement
2.2 if-then-eles语句
当if
语句中的命令返回退出状态码0时,then
部分中的命令会被执行。当if语句中的命令返回非零退出状态码时,bash shell会执行else
部分中的命令。
if command
then command
else command
fi
2.3 嵌套if
可以使用else
部分的另一种形式:elif
。就不用写多个if-then
语句。elif
使用另一个if-then
语句延续else
部分。
$ cat test5.sh
#!/bin/bash
# Testing nested ifs
#
testuser=NoSuchUser
#
if grep $testuser /etc/passwd
then
echo "The user $testuser exists on this system."
else
echo "The user $testuser does not exist on this system."
if ls -d /home/$testuser/
then
echo "However, $testuser has a directory."
fi
fi
$
$ ./test5.sh
The user NoSuchUser does not exist on this system.
/home/NoSuchUser/
However, NoSuchUser has a directory.
if-then-elif
语句语法:
if command1
then commands
elif command2
then more commands
fi
在elif语句中,紧跟其后的else语句属于elif代码块,他并不属于之前的if-then代码块。
可以继续将多个elif
语句串起来,形成一个大的if-then-elif
嵌套组合。
if command1
then command set 1
elif command2
then command set 2
elif command3
then command set 3
elif command4
then command set 4
fi
2.4 test命令
test
命令提供了测试命令退出状态码的功能。如果test
命令中列出的条件成立,test
命令就会退出并返回退出状态码0。
test condition
conditon
是test
命令要测试的一系列参数和值。当使在if-then
语句中使用test
命令时,命令格式如下:
if test condition
then commands
fi
如果不写test
命令的condition
部分,它会以非零的状态退出,并执行else
语句块。
#test测试为真执行then后语句
$ cat test6.sh
#!/bin/bash
# Testing the test command
#
my_variable="Full"
#
if test $my_variable
then
echo "The $my_variable expression returns a True"
#
else
echo "The $my_variable expression returns a False"
fi
$
$ ./test6.sh
The Full expression returns a True
#test测试为假,执行else后语句
$ cat test6.sh
#!/bin/bash
# Testing the test command
#
my_variable=""
#
if test $my_variable
then
echo "The $my_variable expression returns a True"
#
else
echo "The $my_variable expression returns a False"
fi
$
$ ./test6.sh
The expression returns a False
bash shell提供了另一种条件测试方法(使用方括号),无需在if-then
语句中声明test
命令。
if [ condition ]
then commands
fi
方括号定义了测试条件。第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错。test
命令可以判断三类条件:数值比较,字符串比较,文件比较。
2.4.1 数值比较
使用test
命令最常见的情形是对两个数值进行比较数值比较符如下表:
比较 | 描述 |
---|---|
n1 -eq n2 | 检查n1是否与n2相等 |
n1 -ge n2 | 检查n1是否大于或等于n2 |
n1 -gt n2 | 检查n1是否大于n2 |
n1 -le n2 | 检查n1是否小于或等于n2 |
n1 -lt n2 | 检查n1是否小于n2 |
n1 -ne n2 | 检查n1是否不等于n2 |
$ cat numeric_test.sh
#!/bin/bash
# Using numeric test evaluations
#
value1=10
value2=11
#测试value1是否大于5
if [ $value1 -gt 5 ]
then
echo "The test value $value1 is greater than 5"
fi
#测试value1是否与value2相等
if [ $value1 -eq $value2 ]
then
echo "The values are equal"
else
echo "The values are different"
fi
bash shell只能处理整数,基于浮点数的函数不能正确处理。
2.4.2 字符串比较
条件测试还允许比较字符串值,比较符号如下表:
比较 | 描述 |
---|---|
str1 = str2 | 检查str1是否和str2相同 |
str1 != str2 | 检查str1是否和str2不同 |
str1 < str2 | 检查str1是否比str2小 |
str1 > str2 | 检查str1是否比str2大 |
-n str1 | 检查str1的长度是否非0 |
-z str1 | 检查str1的长度是否为0 |
字符串顺序:当时用字符串大于小于号时需进行转义,否则sehell会把字符串当做重定向符号。大于和小于顺序的判断和sort
命令所采用的逻辑不同。
#未对>号进行转义,执行结果错误
$ cat badtest.sh
#!/bin/bash
# mis-using string comparisons
#
val1=baseball
val2=hockey
#
if [ $val1 > $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi
$
$ ./badtest.sh
baseball is greater than hockey
$ ls -l hockey
-rw-r--r-- 1 rich rich 0 Sep 30 19:08 hockey
#对大于号进行转义
$ cat test9.sh
#!/bin/bash
# mis-using string comparisons
#
val1=baseball
val2=hockey
#
if [ $val1 \> $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi
$
$ ./test9.sh
baseball is less than hockey
第一个脚本把大于号解释成了输出重定向。因此,它创建了一个名为hockey的文件。
在比较测试中,大写字母被认为是小于小写字母的,但是**sort
命令恰好相反**。比较测试中使用的是标准的ASCII顺序,根据每个字符的ASCII数值来决定排序结果。
2.4.3 字符串大小
-n
和-z
可以检查一个变量是否含有数据。
$ cat test10.sh
#!/bin/bash
# testing string length
val1=testing
val2=''
#
if [ -n $val1 ]
then
echo "The string '$val1' is not empty"
else
echo "The string '$val1' is empty"
fi
#
if [ -z $val2 ]
then
echo "The string '$val2' is empty"
else
echo "The string '$val2' is not empty"
fi
#
if [ -z $val3 ]
then
echo "The string '$val3' is empty"
else
echo "The string '$val3' is not empty"
fi
$
$ ./test10.sh
The string 'testing' is not empty
The string '' is empty
The string '' is empty
空的和未初始化的变量会shell脚本测试造成严重的影响。如果不是确定一个变量的内容,最好在将其用于数值或字符串比较之前先通过-n
和-z
来测试一下是否含有数值。
2.5 文件比较
文件比较为shell编程中最为强大,也是用的最多的比较形式,文件标记可以测试Linux文件系统上文件和目录的状态。
比较 | 描述 |
---|---|
-d file | 检查file是否存在并是一个目录 |
-e file | 检查file是否存在 |
-f file | 检查file是否存在并是一个文件 |
-r file | 检查file是否存在并可读 |
-s file | 检查file是否存在并非空 |
-w file | 检查file是否存在并可写 |
-x file | 检查file是否存在并可执行 |
-O file | 检查file是否存在并属当前用户所有 |
-G file | 检查file是否存在并默认组与当前用户相同 |
file1 -nt file2 | 检查file1是否比file2新 |
file1 -ot file2 | 检查file1是否比file2旧 |
2.5.1 检查目录
-d
会检查指定的目录是否存在系统中。如果需要将文件写入目录或者是准备切换到某个目录中,先进行-d
检查是好习惯。
$ cat test11.sh
#!/bin/bash
# Look before you leap
#
jump_directory=/home/arthur
#
if [ -d $jump_directory ]
then
echo "The $jump_directory directory exists"
cd $jump_directory
ls
else
echo "The $jump_directory directory does not exist"
fi
2.5.2 检查对象是否存在
-e
检查文件或目录是否存在。
$ cat test12.sh
#!/bin/bash
# Check if either a directory or file exists
#
location=$HOME
file_name="sentinel"
#检查用户是否具有$HOME目录
if [ -e $location ]
then #Directory does exist
echo "OK on the $location directory."
echo "Now checking on the file, $file_name."
#检查$HOME目录中是否存在sentinel文件
if [ -e $location/$file_name ]
then #File does exist
echo "OK on the filename"
echo "Updating Current Date..."
date >> $location/$file_name
#
else #File does not exist
echo "File does not exist"
echo "Nothing to update"
fi
#
else #Directory does not exist
echo "The $location directory does not exist."
echo "Nothing to update"
fi
2.5.3 检查文件
-f
用于检查文件是否存在。
$ cat test13.sh
#!/bin/bash
# Check if either a directory or file exists
#
item_name=$HOME
echo "The item being checked: $item_name"
#
if [ -e $item_name ]
#Item does exist
then
echo "The item, $item_name, does exist."
echo "But is it a file?"
#
if [ -f $item_name ]
#Item is a file
then
echo "Yes, $item_name is a file."
#
else #Item is not a file
echo "No, $item_name is not a file."
fi
#
else #Item does not exist
echo "The item, $item_name, does not exist."
echo "Nothing to update"
fi
2.5.4 检查文件是否可读
使用-r
进行文件可读检查,如果需要从文件中读取数据,最好先测试一下文件是否可读。
$ cat test14.sh
#!/bin/bash
# testing if you can read a file
pwfile=/etc/shadow
#
# first, test if the file exists, and is a file
if [ -f $pwfile ]
then
# now test if you can read it
if [ -r $pwfile ]
then
tail $pwfile
else
echo "Sorry, I am unable to read the $pwfile file"
fi
else
echo "Sorry, the file $file does not exist"
fi
2.5.5 检查空文件
使用-s
检查文件是否为空,删除文件前使用-s检查文件是否为空可以避免删除空文件。当-s比较成功时,说明文件中有数据。
$ cat test15.sh
#!/bin/bash
# Testing if a file is empty
#
file_name=$HOME/sentinel
#
if [ -f $file_name ]
then
if [ -s $file_name ]
then
echo "The $file_name file exists and has data in it."
echo "Will not remove this file."
#
else
echo "The $file_name file exists, but is empty."
echo "Deleting empty file..."
rm $file_name
fi
else
echo "File, $file_name, does not exist."
fi
2.5.6 检查是否可写
-w
判断你对文件是否有可写权限。
if [ -w $item_name ]
then #Item is writable
echo "Writing current time to $item_name"
date +%H%M >> $item_name
#
else #Item is not writable
echo "Unable to write to $item_name"
fi
2.5.7 检查文件是否可执行
-x
判断文件是否有可执行权限。如果要在shell脚本中运行大量脚本,-x
发挥其作用。
$ cat test17.sh
#!/bin/bash
# testing file execution
#
if [ -x test16.sh ]
then
echo "You can run the script: "
./test16.sh
else
echo "Sorry, you are unable to execute the script"
fi
2.5.8 检查所属关系
-O
检查你是否为文件的属主。
$ cat test18.sh
#!/bin/bash
# check file ownership
#
if [ -O /etc/passwd ]
then
echo "You are the owner of the /etc/passwd file"
else
echo "Sorry, you are not the owner of the /etc/passwd file"
fi
2.5.9 检查默认属组关系
-G
检查文件的默认组,如果文件的默认组和用户默认组相同,则测试成功。-G
只会检查默认组而非用户所属的所有组。
$ cat test19.sh
#!/bin/bash
# check file group test
#
if [ -G $HOME/testing ]
then
echo "You are in the same group as the file"
else
echo "The file is not owned by your group"
fi
2.5.10 检查文件日期
-nt
会判定一个文件是否比另一个文件新。如果文件比较新,那意味着该文件创建日期距离当下时间更近。-ot
比较会判定一个文件是否比另一个文件旧,文件更旧意味着创建日期更早。
$ cat test20.sh
#!/bin/bash
# testing file dates
#
if [ test19.sh -nt test18.sh ]
then
echo "The test19 file is newer than test18"
else
echo "The test18 file is newer than test19"
fi
if [ test17.sh -ot test19.sh ]
then
echo "The test17 file is older than the test19 file"
fi
用于比较文件路径是相对于你运行该脚本的目录而言。-nt
和-ot
选项都不会检查文件是否存在,所以在使用这两个选项前,先使用-f
确定文件是否存在。
2.6 复合条件测试
if-then
语句允许你使用布尔逻辑来组合测试,有两种布尔运算符可用:
[ condition1 ] && [ condition2 ]
[ condition1 ] || [ condition2]
第一种使用AND
运算符来组合两个条件,即两个条件都满足是执行then
部分命令。
第二种使用OR
运算符来组合两个条件,当任一条件满足时执行then
部分命令。
$ cat test22.sh
#!/bin/bash
# testing compound comparisons
#
if [ -d $HOME ] && [ -w $HOME/testing ]
then
echo "The file exists and you can write to it"
else
echo "I cannot write to the file"
fi
2.7 if-then高级特性
bash shell提供了两项可以在if-then
语句中使用的高级特性:
- 用于数学表达式的双括号
- 用于高级字符串处理功能的双方括号
2.7.1 双括号
双括号命令可以在比较的过程中使用高级数学表达式。test命令只能在比较的过程中使用简单的算术操作。双括号提供了更多的数学符号。
#双括号语法
(( expression ))
expression
可以是任意的数学赋值或比较表达式,常用数学表达式符号如下表:
符号 | 描述 |
---|---|
val++ | 后增 |
val– | 后减 |
++val | 先增 |
–val | 先减 |
! | 逻辑求反 |
~ | 位求反 |
** | 幂运算 |
<< | 左位移 |
>> | 右位移 |
& | 位布尔和 |
| | 位布尔或 |
&& | 逻辑和 |
|| | 逻辑或 |
可以在if
语句中用双括号命令,也可以在脚本中的普通命令里来赋值。
$ cat test23.sh
#!/bin/bash
# using double parenthesis
#
val1=10
#
if (( $val1 ** 2 > 90 ))
then
(( val2 = $val1 ** 2 ))
echo "The square of $val1 is $val2"
fi
不需要对双括号中表达式里的大于号转义。这是双口号提供的另一个高级特性。
2.7.2 双方括号
提供了针对字符串比较的高级特性。
#语法如下
[ [expression] ]
双方括号里的expression
使用了test
命令中采用的标准字符串比较。同时也提供了test
未提供的特性:模式匹配(pattern matching)
。在模式匹配中,可以定义一个正则表达式来匹配字符串值。
$ cat test24.sh
#!/bin/bash
# using pattern matching
#
#能识别r开头的字符串
if [[ $USER == r* ]]
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
2.8 case命令
case
命令可以替换判断较多的if-then-else
语句。
$ cat test25.sh
#!/bin/bash
# looking for a possible value
#
if [ $USER = "rich" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "barbara" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "testing" ]
then
echo "Special testing account"
elif [ $USER = "jessica" ]
then
echo "Do not forget to logout when you're done"
else
echo "Sorry, you are not allowed here"
fi
case命令语法:
case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac
case
命令会将指定的变量与不同模式进行比较。如果变量和模式是匹配的,那么shell会执行为该模式指定的命令。
$ cat test26.sh
#!/bin/bash
# using the case command
#
case $USER in
rich | barbara)
echo "Welcome, $USER"
echo "Please enjoy your visit";;
testing)
echo "Special testing account";;
jessica)
echo "Do not forget to log off when you're done";;
*)
echo "Sorry, you are not allowed here";;
esac
本章小结
1.介绍了结构化语句(if语句相关)
2.介绍了test命令,方括号条件测试
3.介绍了文件比较命令
4.复合条件测试
5.if语句的高级特性
6.case命令
参考文献:Linux命令行与shell脚本编程大全(第三版)Richard Blum Christine Bresnahan著 门佳 武海峰译