参考书目:《Linux命令行与shell脚本编程大全》
文章内容如下:
- 使用 if-then 语句
- 嵌套 if 语句
- test 命令
- 复合条件测试
- 使用双方括号和双括号
- case 命令
文章目录
1、if-then 语句
在shell语言中,最基本的结构化命令是 if-then 语句。if-then 语句基本格式如下:
if command
then
commands
fi
bash shell 的 if 语句会运行 if 之后的命令。如果该命令的退出状态码为0(命令成功运行),那么位于 then 部分的命令就会被执行。如果该命令的退出状态码是其他值,则 then 部分命令不会被执行,bash shell 会接着处理脚本中的下一条命令。fi 语句用来表示 if-then 语句到此为止。
关于退出状态码
对于成功结束的命令,其退出状态码是 0。对于因错误而结束的命令,其退出状态码是一个0~255的正整数。
例:
#!/bin/bash
if pwd
then
echo "it worked."
fi
该脚本在 if 行中使用了 pwd 命令。如果命令成功结束,那么 echo 语句就会显示字符串。在命令行中运行脚本,得到如下结果:
wsy@localhost:~/Shell$ ./test1.sh
/home/wsy/Shell
it worked.
shell 执行了 if 行中的 pwd 命令。由于退出状态码是0,因此它也执行了 then 部分的 echo 语句。
再来查看一个退出状态码非0的例子:
#!/bin/bash
if IamNotaCommand
then
echo "it worked."
fi
echo "We are outside the if statement"
本例中,我们在 if 语句中故意使用了一个不存在的命令 IamNotaCommand
。由于这是个错误的命令,因此会产生一个非0的退出状态码,bash shell 因此跳过了 then 部分的 echo 命令,但要注意的是,if 语句中的那个错误命令执行所产生的消息依然会显示在脚本的输出中。
执行结果如下:
wsy@localhost:~/Shell$ ./test2.sh
./test2.sh:行3: IamNotaCommand: 未找到命令
We are outside the if statement
注意:
if-then 语句还有另一种形式,在有些脚本中也会经常遇到:
if command; then
commands
fi
能出现在 then 部分的命令可不止一条,bash shell 会将列在 then 部分的所有命令视为一个代码块,如果 if 语句行命令的退出状态码为 0,那么代码块中的所有命令都会被执行;否则会跳过整个代码块:
#!/bin/bash
testuser=wsy
if grep $testuser /etc/passwd
then
echo "This is my first command in the then block."
echo "This is my second command in the then block."
echo "I can even put in other commands besides echo:"
ls /home/$testuser
fi
echo "We are outside the if statement"
if 语句行使用grep命令在 /etc/passwd 文件中查找系统中是否存在某个特定用户,如果存在,则脚本会显示一些文本信息并列出该用户 $HOME 目录中的文件:
wsy@localhost:~/Shell$ ./test3.sh
wsy:x:1000:1000:wsy:/home/wsy:/bin/bash
This is my first command in the then block.
This is my second command in the then block.
I can even put in other commands besides echo:
anaconda3 Shell Win_Shared WSY 公共 模板 视频 图片 文档 下载 音乐 桌面
We are outside the if statement
但是,如果将 testuser 变量设置成一个系统中不存在的用户,则不会执行 then 代码块中的任何命令:
wsy@localhost:~/Shell$ cat test3.sh
#!/bin/bash
testuser=www
if grep $testuser /etc/passwd
then
echo "This is my first command in the then block."
echo "This is my second command in the then block."
echo "I can even put in other commands besides echo:"
ls /home/$testuser
fi
echo "We are outside the if statement"
wsy@localhost:~/Shell$ ./test3.sh
We are outside the if statement
2、if-then-else 语句
在 if-then 语句中,不管命令是否执行成功,都只有一种选择。如果命令返回一个非0退出状态码,则bash shell 会继续执行脚本中的下一条命令。在这种情况下,如果能够执行另一组命令就好了,这正是 if-then-else 语句的作用,格式如下:
if command
then
commands
else
commands
fi
当 if 语句中的命令退出状态码为0时,then 部分的命令会被执行,这跟普通的 if-then 语句一样。当 if 语句中的命令返回非0退出状态码时,bash shell 会执行 else 部分中的命令。
例:
#!/bin/bash
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The files in the hone directory of $testuser are:"
ls /home/$testuser
echo
else
echo "The user $testuser does not exist on this system."
echo
fi
echo "We are outside the if statement."
执行如下:
wsy@localhost:~/Shell$ ./test4.sh
The user NoSuchUser does not exist on this system.
We are outside the if statement.
3、嵌套 if 语句
使用场景:检查多种条件
例如:要检查 /etc/passwd 文件中是否存在某个用户以及该用户的主目录是否存在,可以使用嵌套的 if-then 语句。嵌套部分位于 if-then-else 语句的 else 代码块中:
#!/bin/bash
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The user $testuser account exists on this system."
echo
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
echo "We are outside the nested statements."
先在 /home 目录下创建一个NoSuchUser文件夹,保证else代码块中的then语句部分可以执行到,然后再运行脚本:
wsy@localhost:~/Shell$ sudo mkdir -p /home/NoSuchUser
[sudo] wsy 的密码:
wsy@localhost:~/Shell$ ls -d /home/NoSuchUser/
/home/NoSuchUser/
wsy@localhost:~/Shell$ ./test5.sh
The user NoSuchUser does not exist on this system.
/home/NoSuchUser
However,NoSuchUser has a directory.
We are outside the nested statements.
这个脚本准确无误地发现,虽然用户账户已经从 /etc/passwd 中删除了,但该用户的目录仍然存在。
你可以使用 else 部分的另一种名为 elif 的形式,这样就不用再写多个 if-then 语句了,elif 使用另一个 if-then 语句延续 else 部分:
if command1
then
commands
elif command2
then
more commands
fi
elif 语句行提供了另一个要测试的命令,这类似于原始的 if 语句行。如果 elif 之后的命令的退出状态码为 0,则 bash 会执行第二个 then 语句部分的命令(当然,前提是上面的 if 没有执行),这种嵌套形式使得代码更清晰,逻辑更易懂:
#!/bin/bash
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The user $testuser account exists on this system."
echo
elif ls -d /home/$testuser
then
echo "The user $testuser has a directory,"
echo "even though $testuser doesn't have an account."
fi
echo "We are outside the nested statements."
执行脚本:
wsy@localhost:~/Shell$ ./test5.sh
/home/NoSuchUser
The user NoSuchUser has a directory,
even though NoSuchUser doesn't have an account.
We are outside the nested statements.
该脚本目前还存在一个问题:如果指定账户及其主目录都不存在了,那么脚本不会发出任何提醒,为此,我们进一步修改脚本,在嵌套 elif 中加入一个 else 语句来实现:
#!/bin/bash
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The user $testuser account exists on this system."
echo
elif ls -d /home/$testuser
then
echo "The user $testuser has a directory,"
echo "even though $testuser doesn't have an account."
else
echo "The user $testuser doesn't exist on this system,"
echo "and no directory exists for the $testuser."
fi
echo "We are outside the nested statements."
执行脚本:
wsy@localhost:~/Shell$ ./test5.sh
ls: 无法访问/home/NoSuchUser: 没有那个文件或目录
The user NoSuchUser doesn't exist on this system,
and no directory exists for the NoSuchUser.
We are outside the nested statements.
在 /home/NoSuchUser 目录被删除之前,这个测试脚本执行的是 elif 语句,返回0值的退出状态,因此 elif 的 then 代码块中的语句得以执行。删除 /home/NoSuchUser 目录之后,elif 语句返回的是非0值的状态码,这使得 elif 块中的 else 代码块得以执行。
注:在 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
4、test 命令
if-then 命令不能测试退出状态码之外的条件,但是 bash shell 中有个好用的工具即 test 可以在if-then 语句中测试不同的条件。如果 test 命令中列出的条件成立,那么 test 命令就会退出并返回退出状态码0,这样 if-then 的工作方式就可用了。
test 命令格式很简单:
test condition
condition 是 test 命令要测试的一系列参数和值。当用在 if-then 语句中时,test 命令看起来如下所时:
if test condition
then
commands
fi
如果不写 test 命令的 condition 部分,则它会以非0状态码退出并执行else代码块:
#!/bin/bash
if test
then
echo "No expression returns a True."
else
echo "No expression returns a False."
fi
执行脚本:
wsy@localhost:~/Shell$ ./test6.sh
No expression returns a False.
如果加入了测试条件,则test会测试该条件,例如可以使用 test 命令确定变量是否为空:
#!/bin/bash
val="Full"
if test $val
then
echo "The variable has content and returns a True."
echo "The variable content is: $val."
else
echo "The variable doesn't have content and returns a False."
fi
执行脚本:
wsy@localhost:~/Shell$ ./test6.sh
The variable has content and returns a True.
The variable content is: Full.
由于变量 val 中包含内容 Full,因此当 test 命令测试条件时,返回的退出状态码为 0,这使得 then 语句块中的语句得以执行。当然,如果变量中没有包含内容,就会出现相反的情况:
#!/bin/bash
val=""
if test $val
then
echo "The variable has content and returns a True."
echo "The variable content is: $val."
else
echo "The variable doesn't have content and returns a False."
fi
执行脚本:
wsy@localhost:~/Shell$ ./test6.sh
The variable doesn't have content and returns a False.
bash shell 提供了另一种测试条件方式,无需在 if-then 语句中写明 test 命令:
if [ condition ]
then
commands
fi
注意:第一个方括号之后和第二个方括号之前必须留有空格,否则就会出现语法错误。
test 命令和测试条件可以判断3类条件。
- 数值比较
- 字符串比较
- 文件比较
4.1 数值比较
使用 test 命令最常见的情形是对两个数值进行比较,下表列出了测试两个数值时可用的条件参数。
数值条件测试可用于测试数字和变量,如:
#!/bin/bash
var1=10
var2=11
if [ $var1 -gt 5 ]
then
echo "The test value $var1 is greater then 5."
else
echo "The test value $var1 is less then or equal to 5."
fi
if [ $var1 -eq $var2 ]
then
echo "The values are equal."
else
echo "The values are different."
fi
执行脚本:
wsy@localhost:~/Shell$ ./numeric_test.sh
The test value 10 is greater then 5.
The values are different.
注意:对于条件测试,bash shell 只能处理整数。
4.2 字符串比较
- 字符串相等性
字符串的相等和不等条件不言自明,很容易看出两个字符串是否相同:
#!/bin/bash
testuser=wsy
if [ $testuser = wsy ]
then
echo "The testuser variable contains: wsy"
else
echo "The testuser variable: $testuser "
fi
执行脚本:
wsy@localhost:~/Shell$ ./string_test.sh
The testuser variable contains: wsy
- 字符串顺序
要测试一个字符串是否大于或小于另一个字符串就开始变得棘手了,必须注意以下问题:
- 大于号和小于号必须转义,否则 shell 会将其视为重定向,将字符串值当作文件名;
- 大于和小于顺序与 sort 相反。
我们先看大于号和小于号必须转义的问题:
#!/bin/bash
string1=wsy
string2=cxs
if [ $string1 > $string2 ]
then
echo "$string1 is greater then $string2 ."
else
echo "$string1 is less then $string2 ."
fi
这个脚本中只用了大于号,虽然没有出错,但结果不对。脚本把大于号解释成了输出重定向,因此创建了一个 cxs 的文件,由于重定向顺利完成了,测试条件返回退出状态码0,if 语句便认为条件成立。
执行脚本:
wsy@localhost:~/Shell$ ./bad_string_comparison.sh
wsy is greater then cxs .
wsy@localhost:~/Shell$ ls c*
cxs
要解决这个问题,需要使用反斜线(\)正确地转义大于号:
#!/bin/bash
string1=wsy
string2=cxs
if [ $string1 \> $string2 ]
then
echo "$string1 is greater then $string2 ."
else
echo "$string1 is less then $string2 ."
fi
执行脚本:
wsy@localhost:~/Shell$ rm -f cxs
wsy@localhost:~/Shell$ ./bad_string_comparison.sh
wsy is greater then cxs .
wsy@localhost:~/Shell$ ls c*
ls: 无法访问c*: 没有那个文件或目录
现在的答案才是我们想要的。
注意:字符串 wsy 大于 cxs,是因为在比较的时候使用的是每个字符的 Unicode 编码值。小写字母 w 的编码值是 119,而 c 的编码值是 99。因此,w 大于 c,进而 wsy 大于 cxs。
下面我们再看大于和小于顺序与 sort 相反的问题,这个为题更加细微,除非经常处理大小写字母,否则几乎遇不到。sort 命令处理大写字母的方法正好与 test 相反:
wsy@localhost:~/Shell$ cat file.txt
Score
score
wsy@localhost:~/Shell$ sort file.txt
score
Score
wsy@localhost:~/Shell$ cat sort_older_comparison.sh
#!/bin/bash
string1=Score
string2=score
if [ $string1 \> $string2 ]
then
echo "$string1 is greater then $string2."
else
echo "$string1 is less then $string2."
fi
wsy@localhost:~/Shell$ ./sort_older_comparison.sh
Score is less then score.
wsy@localhost:~/Shell$
在比较测试中,大写字母被认为是小于小写字母的,但 sort 命令正好相反。当你将同样的
字符串放进文件中并用 sort 命令排序时,小写字母会先出现。这是由于各个命令使用了不同的
排序技术。
比较测试中使用的是标准的 Unicode 顺序,根据每个字符的 Unicode 编码值来决定排序结果。
sort 命令使用的是系统的语言环境设置中定义的排序顺序。对于英语,语言环境设置指定了在
排序顺序中小写字母出现在大写字母之前。
- 字符串大小
-n 和 -z 可以很方便地用于检查一个变量是否为空:
#!/bin/bash
string1=Score
string2=''
if [ -n $string1 ]
then
echo "The string '$string1' is NOT empty."
else
echo "The string '$string1' IS empty."
fi
if [ -z $string2 ]
then
echo "The string '$string2' IS empty."
else
echo "The string '$string2' is NOT empty."
fi
if [ -z $string3 ]
then
echo "The string '$string3' IS empty."
else
echo "The string '$string3' is NOT empty."
fi
执行脚本:
wsy@localhost:~/Shell$ ./variable_content_val.sh
The string 'Score' is NOT empty.
The string '' IS empty.
The string '' IS empty.
如表中所示,-n 用于检测字符串长度是否不为0,而 -z 用于检测字符串长度是否为0。
注意:上述代码中,变量 string3 并为定义,但是脚本仍然可以判断它,并将其视为长度为0的字符串。
4.3 文件比较
例如:
#!/bin/bash
location=$HOME
file_name="sentinel"
if [ -d $location ]
then
echo "OK on the $location directory."
echo "Now checking on the file, $file_name..."
if [ -e $HOME/$file_name ]
then
echo "OK on the file,$file_name."
echo "Updating file's contents."
date >> $location/$file_name
else
echo "File, $location/$file_name, does NOT exist."
echo "Nothing to update."
fi
else
echo "Directory, $location, does NOT exist."
echo "Nothing to update."
fi
上述代码首先使用 -d 测试用户的 $HOME 目录是否存在。如果存在,那么接下来的 -e 测试会检查 sentinel 文件是否存在于 $HOME 目录下。如果文件不存在,则 shell 脚本会提示该文件缺失,不需要进行更新,如果存在,脚本会将当前日期写入该文件。
执行脚本:
wsy@localhost:~/Shell$ ./update_file.sh
OK on the /home/wsy directory.
Now checking on the file, sentinel...
File, /home/wsy/sentinel, does NOT exist.
Nothing to update.
为了确保更新正常进行,下面先创建 sentinel 文件,然后重新运行脚本:
wsy@localhost:~/Shell$ touch $HOME/sentinel
wsy@localhost:~/Shell$ ./update_file.sh
OK on the /home/wsy directory.
Now checking on the file, sentinel...
OK on the file,sentinel.
Updating file's contents.
wsy@localhost:~/Shell$ cat $HOME/sentinel
2024年 01月 15日 星期一 21:33:26 CST
5、复合条件测试
if-then 语句允许使用布尔逻辑将测试条件组合起来。可以使用以下两种布尔运算符:
-
[ condition1 ] && [ condition2 ]
-
[ condition1 ] || [ condition2 ]
&&(AND) 与 ||(OR) 运算符与其他语法中起到的作用一样,下面来演示 AND 布尔运算符法用法:
#!/bin/bash
if [ -d $HOME ] && [ -w $HOME/Shell/newfile ]
then
echo "The file exists and you can write to it."
else
echo "You can not write to the file."
fi
执行脚本,先在不存在 newfile 文件的情况下执行,然后手动创建 newfile 文件后再重新执行脚本:
wsy@localhost:~/Shell$ ls -l newfile
ls: 无法访问newfile: 没有那个文件或目录
wsy@localhost:~/Shell$ ./AndBoolean.sh
You can not write to the file.
wsy@localhost:~/Shell$ touch newfile
wsy@localhost:~/Shell$ ls -l newfile
-rw-rw-r--. 1 wsy wsy 0 1月 16 10:14 newfile
wsy@localhost:~/Shell$ ./AndBoolean.sh
The file exists and you can write to it.
wsy@localhost:~/Shell$
6、if-then 的高级特性
未完待续。。。