说到shell脚本的结构化命令,我们不得不首先了解一下退出状态码的概念。shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。退出状态码是一个0-255的整数值,在命令结束运行时由命令传给shell。我们可以获取这个值并在脚本中使用。
Linux中专门提供了变量$?来保存上个已执行的命令的退出码,它的值是shell所执行的最后一条命令的退出状态码。我们可以尝试一下使用不同命令其退出状态码是什么。
命令成功结束时,其退出状态码为0;如果有错误,退出码就是一个正整数值,如126表示命令不可执行、127表示没找到命令、128表示无效的参数等等。
exit命令:
默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出,我们可以用exit n
来自定义状态码,注意n要在0-255之间。
一、if-then语句
if-then语句格式如下:
if command
then
commands
fi
在bash shell中if后面的是一个命令,如果该命令的退出状态码是0(说明该命令执行成功),那么位于then后面的命令才会被执行。注意,这和其他编程语言的if语句返回true或false不一样。只要关注了这个,这个语句使用起来很容易:
二、if-then-else语句
在if-then语句中不管命令是否执行成功,你都只有一种选择:成功则执行then中的命令;命令失败则继续往下执行。有的情况下,我们需要使用if-then-else语句:
if command
then
commands
else
commands
fi
这个语句的使用也很直观,请看以下示例:
三、嵌套if
如果使用上面的语句仍然不能很多好的满足要求,那我们可以使用嵌套if来工作:
if command1
then
commands
elif command2
then
commands2
fi
其实这里只是将else里面又嵌入了一个if-then语句,只是将else-if连起来写成了elif;我们可以随意组合嵌套,完成更复杂的逻辑。这里就不做示例了,很简单,并且后面经常会用到。
四、test命令
到目前为止,在if中用到的命令都是普通的shell命令,那么if后面是否可以测试出退出状态码以外的条件呢? 是不能的,但是我们可以用test命令来测试其他条件,只要test命令中列出的条件成立,test命令就会退出并返回退出状态码0,这样就可以完成类似于其他语言中那样的if-then语句了。
test命令的格式非常简单:test condition
,condition是命令要测试的一系列参数和值相当于我们用test命令代替了之前的command命令。
如上图所示,我们在test命令里面测试一个存在的变量,会返回0,使得then中的语句会执行;相反则返回的退出状态码不为0。
test命令还有另一种常用的简便写法:单方括号 [ condition ]
,注意方括号距字符串必须加上一个空格。test命令可以用来判断三类条件:
- 数值比较
- 字符串比较
- 文件比较
下面我们来学习这几种条件测试的使用方法,后面也会经常用到这些。
1、数值比较:
数值比较功能如下表,可以用在数字和变量上。
比较 | 描述 |
---|---|
n1 -eq n2 | 检查n1与n2是否相等(equal) |
n1 -ge n2 | 检查n1是否大于或等于n2(greater、equal) |
n1 -gt n2 | 检查n1是否大于n2(greater than) |
n1 -le n2 | 检查n1是否小于或等于n2(less、equal) |
n1 -lt n2 | 检查n1小于n2(less than) |
n1 -ne n2 | 检查n1与n2是否不相等(not equal) |
~
需要记住,bash shell只能处理整数。
2、字符串比较:
比较 | 描述 |
---|---|
str1 = str2 | 检查str1和str2是否相同 |
str1 != str2 | 检查str1和str2是不同 |
str1 < str2 | 检查str1是否小于str2 |
str1 > str2 | 检查str1是否大于str2 |
-n str | 检查str的长度是否非0 |
-z str | 检查str长度是否为0 |
~
这里需要注意两个问题:
- 大于号和小于号必须转移,否则shell会把它们当做重定向符号,把字符串当做文件名
- 大于小于的排序是按照ASCII码的顺序(即由小到大为0~9,A~Z,a~z)
我们编辑如下的脚本,用来检测变量是否长度为0。
#!/bin/bash
value1=testing
value2=""
if [ -n $value1 ]
then
echo "Then string $value1 not empty"
else
echo "The string $value1 is empty"
fi
if [ -z $value2 ]
then
echo "The string $value2 is empty"
else
echo "The string $value2 is not empty"
fi
if [ -z $value3 ]
then
echo "The string $value3 is empty"
else
echo "The string $value3 is not empty"
fi
执行输出后,我们发现:定义了数值的变量长度不为0;而定义为空和未定义的变量其长度均为0。
Then string testing not empty
The string is empty
The string is empty
空的和为初始化的变量会对我们的脚本造成很大的影响。如果不确定其值的内容,在使用之前,最好可以用-n或-z测试一下是否含有值。
3、文件比较:
文件比较是shell编程中很强大也最常用的比较形式,它允许你测试Linux文件系统上的文件和目录的状态。比较形式如下表:
比较 | 描述 |
---|---|
-d file | file是否存在并是一个目录 |
-f file | file是否存在并是一个文件 |
-e file | file是否存在 |
-r file | file是否存在并且可读 |
-w file | file是否存在并且可写 |
-s file | file是否存在并且非空 |
-x file | file是否存在并且可执行 |
-O file | file是否存在并且属当前用户所有 |
-G file | file是否存在并且默认组和当前用户相同 |
file1 -nt file2 | file1是否比file2新(new than) |
file1 -ot file2 | file1是否比file2旧(old than) |
~
这些条件使用起来也是比较简单的,我们应当先搞清楚逻辑再去做一系列的测试。
如上所示,这里我们直接比较了两个文件的新旧,然而我们并没有确认这连个文件是否存在,如果没有存在的话,就可能得到一个错误的结果。我们可以使用布尔运算符来组合测试:
#复合条件测试
[ condition1 ] && [ condition2 ]
[ condition1 ] || [ condition2 ]
五、使用双小括号和双方括号
双小括号:
使用双小括号来使用高级数学表达式,提供了很多其他编程语言类似的运算符。格式如下:
(( expression ))
能够用在这里的表达式很多,比如:i++、i–、++i、–i之类的;逻辑求反!;一些布尔运算和位运算等等。在其他编程语言中可以用于的数学赋值或比较表达式等都可以用。
双方括号:
双方括号命令提供了针对字符串比较的高级特性。双方括号里面的表达式使用了test命令中采用的标准字符串比较。并且提供了模式匹配这个强大的特性。格式如下:
[[ expression ]]
我们再来总结一下几种括号的使用:
方括号[ ]:类似于test命令,用判断三类条件:数值、字符串、文件
双小括号(( )):在比较中使用高级数学表达式
双方括号[[ ]]:字符串比较的升级-模式匹配(并不是所有shell都支持?)
六、case命令-简化if-then-else
当我们尝试在一组可能的值中寻找特定值,再来进行其他操作时。可能需要写下很长的if-then-else语句,这时可以使用case语句来简化脚本,case命令的格式如下:
#可以通过或操作符|在一行中使用多个pattern
case variable in
pattern1) command1;;
pattern2) command2;;
*) default commands;;
esac
上面这个简单的示例演示了获取当前目录并使用case来判断在哪个目录里面。不难发现使用case的场景还是比较多的。
通过这一篇大致的熟悉了结构化命令中的跳转语句,还有循环语句等者我们去学习呢。下篇预告:for/while/until等循环语句的使用。 :)