Shell编程——条件测试与比较

变量的数值计算

1、Shell脚本的条件测试

1.1、条件测试方法综述

通常,在bash的各种条件结构和流程控制结构中都要进行各种测试,然后根据测试结果执行不同的操作,有时也会与if等条件语句相结合,来完成测试判断,以减少程序运行的错误。

执行条件测试表达式后通常会返回"真"或"假"​,就像执行命令后的返回值为0表示真,非0表示假一样。

在bash编程里,条件测试常用的语法形式见表。

针对表有几个注意事项需要说明一下:

  • 语法1中的test命令和语法2中的\[\]是等价的。语法3中的\[]为扩展的test命令,语法4中的(())常用于计算,建议使用相对友好的语法2,即中括号(\[\])的语法格式。
  • \[](双中括号)中可以使用通配符等进行模式匹配,这是其区别于其他几种语法格式的地方。
  • &&、||、>、<等操作符可以应用于\[]中,但不能应用于\[\]中,在\[\]中一般用-a、-o、-gt(用于整数)、-lt(用于整数)代替上述操作符。
  • 对于整数的关系运算,也可以使用Shell的算术运算符(())。

1.2、test条件测试的简单语法及示例

test条件测试的语法格式为:

bash 复制代码
test <测试表达式>

对于如下语句:

bash 复制代码
test -f file && echo true || echo false

该语句表示如果file文件存在,则输出true,否则(||)输出false。这里的&&是并且的意思。test的-f参数用于测试文件是否为普通文件,test命令若执行成功(为真)​,则执行&&后面的命令,而||后面的命令是test命令执行失败之后(为假)所执行的命令。

test命令测试表达式的逻辑也可以用上述表达形式的一半逻辑(即仅有一个&&或||)来测试,示例如下。

bash 复制代码
test -f /tmp/oldboy.txt && echo 1 #<==若表达式成功,则输出1。
test -f /tmp/oldboy.txt || echo 0 #<==若表达式不成功,则输出0。

另外,逻辑操作符&&和||的两端既可以有空格,也可以无空格,这主要看读者习惯。老男孩的习惯是,尽量减少输入空格,因此在逻辑操作符&&和||的两端会尽量不输入空格,主要是考虑输入应简洁、快速,当然,带了空格看起来会更美观一些。在写本书时,老男孩也曾为究竟应使用哪种方式纠结了很久,最后终于决定使用带空格的语法。

test测试语法的格式说明如图所示。

范例:在test命令中使用-f选项(文件存在且为普通文件则表达式成立)测试文件。

bash 复制代码
[root@oldboy ~]# test -f file && echo true || echo false
#<==如果file文件存在并且是普通文件就为真,因为file文件不存在,所以输出了false。
false
[root@oldboy ~]# touch file        #<==现在创建不存在的普通文件file。
[root@oldboy ~]# test -f file && echo true || echo false #<==因为file文件存在,所以输出了true。
true

范例:在test命令中使用-z选项(如果测试字符串的长度为0,则表达式成立)测试字符串。

bash 复制代码
[root@oldboy ~]# test -z "oldboy" && echo 1 || echo 0
#<==如果测试字符串的长度为0,则表达式成立,因为被测试的字符串为oldboy,不为0,表达式结果为假,因此返回0。
0
[root@oldboy ~]# char="oldboy" #<==将oldboy字符串赋值给变量char。
[root@oldboy ~]# test -z "$char" && echo 1 || echo 0 #<==对变量char进行测试,注意要带$。
0
[root@oldboy ~]# char=""        #<==将空值赋值给变量char。
[root@oldboy ~]# test -z "$char" && echo 1 || echo 0
#<==如果测试字符串的长度为0,则表达式成立,因为被测试的字符串为空,因此表达式的结果为真,返回1。
1

提示:关于test测试表达式的更多知识可执行man test查看帮助,本书大部分场景都会使用的语法替代test命令的语法。

结论:test命令测试的功能很强大,但是和​、​​\[​​]的功能有所重合,因此,在实际工作中选择一种适合自己的语法就好了。对于其他的语法,能读懂别人写的脚本就可以了。

1.3、​(中括号)条件测试语法及示例

条件测试的语法格式为:

bash 复制代码
​[ <测试表达式> ]

注意:中括号内部的两端要有空格,​和test等价,即test的所有判断选项都可以直接在里使用。

对于如下语句:

bash 复制代码
[ -f /tmp/oldboy.txt ] && echo 1 || echo 0

如果/tmp/oldboy.txt文件存在,则输出1,否则(||)输出0。这里的&&表示并且。​的应用同test命令,若中括号里的命令执行成功(返回真)​,则执行&&后面的命令,否则执行||后面的命令。

测试表达式的逻辑也可以用如下的语法来判断逻辑的表达式写法(test命令的用法也适合于此)​,即:

bash 复制代码
[ -f /tmp/oldboy.txt ] && echo 1 #<==若表达式成功,则输出1。
[ -f /tmp/oldboy.txt ] || echo 0 #<==若表达式不成功,则输出0。

另外,逻辑操作符&&和||的两端可以有空格也可以无空格,本书同样使用带空格的语法形式。

测试语法的格式说明如图所示。

范例:利用加-f选项(文件存在且为普通文件则表达式成立)测试文件。

bash 复制代码
[root@oldboy ~]# [ -f /tmp/oldboy.txt ] && echo 1 || echo 0
 #<==如果/tmp/oldboy.txt文件存在并且是普通文件则为真,因为该文件不存在,所以输出了0。
0
[root@oldboy ~]# touch /tmp/oldboy.txt #<==创建文件。
[root@oldboy ~]# [ -f /tmp/oldboy.txt ] && echo 1 || echo 0
1 #<==因为文件存在,所以输出1。
[root@oldboy ~]# [ -f /tmp/oldboy.txt ] && echo 1
 #<==可以只写前一半(即只有&&部分),如果文件存在则输出1。
1
[root@oldboy ~]# [ -f /tmp/oldboy.txt ] || echo 0
 #<==可以只写后一半(即只有||部分),如果文件不存在则输出0,因为文件存在,所以没有输出。
[root@oldboy ~]# [ -f /tmp/oldgirl.txt ] || echo 0
 #<==如果文件不存在,则输出0,因为oldgirl.txt不存在,所以输出了0。
0

提示:​命令的选项和test命令的选项是通用的,因此,使用时的参数选项可以通过man test命令获得帮助。

1.4、​\[​​]条件测试语法及示例

​\[​​]条件测试的语法格式为:

bash 复制代码
​[​[ <测试表达式> ]​]​。

注意:双中括号里的两端也要有空格。

对于如下语句:

bash 复制代码
[[ -f /tmp/oldboy.txt ]] && echo 1 || echo 0

如果/tmp/oldboy.txt文件存在就输出1,否则(||)就输出0。这里的&&表示并且。​​\[​​]的应用属于和test命令的扩展命令,功能更丰富也更复杂。如果双中括号里的表达式成立(为真)​,则执行&&后面的命令,否则执行||后面的命令。

​\[​​]测试表达式的逻辑也可以使用如下的部分逻辑形式,即:

bash 复制代码
[[ -f /tmp/oldboy.txt ]] && echo 1    #<==若表达式成功则输出1。
[[ -f /tmp/oldboy.txt ]] || echo 0    #<==若表达式不成功则输出0。

另外,逻辑操作符&&和||的两端可以有空格也可以无空格,本书使用的是带空格的语法形式。

双中括号内部的两端要有空格,​​\[​​]里的测试判断选项,也可以通过man test来获得,​​\[​​]表达式与和test用法的选项部分是相同的,其与和test测试表达式的区别在于,在​\[ ​]中可以使用通配符等进行模式匹配;并且&&、||、>、<等操作符可以应用于​\[ ​]中,但不能应用于 中,在 中一般使用-a、-o、-gt(用于整数)​、-lt(用于整数)等操作符代替上文提到的用于​\[​​]中的符号。除了使用通配符功能之外,建议放弃这个双中括号的写法,虽然它是较新的test命令的语法格式。

​\[​​]测试语法的格式说明如图所示。

范例:​\[​​]的使用示例。

bash 复制代码
[root@oldboy ~]# [[ -f /tmp/oldgirl.txt ]] || echo 0
#<==如果/tmp/oldgirl.txt文件存在并且是普通文件,则为真,因为文件不存在,所以输出了0。
0
[root@oldboy ~]# touch /tmp/oldgirl.txt #<==创建oldgirl.txt,注意/tmp目录。
[root@oldboy ~]# [[ -f /tmp/oldgirl.txt ]] || echo 0 #<==因为文件存在了,所以后半部分没有输出。
[root@oldboy ~]# [[ -f /tmp/oldboy.txt ]] || echo 0 #<==因为存在/tmp/oldboy.txt,所以后半部分没有输出。
[root@oldboy ~]# rm -f /tmp/oldboy.txt              #<==删除oldboy.txt文件。
[root@oldboy ~]# [[ -f /tmp/oldboy.txt ]] || echo 0 #<==因为文件不存在了,所以输出0。
0

有关test、​​、​​\[​​]这些操作符的用法,通过help test或man test查询即可得到帮助,完整的​、​​\[​​]用法可通过man bash来获取。

2、文件测试表达式

2.1、文件测试表达式的用法

在讲解文件测试表达式之前,先举一个生活中的例子:如果你要找老男孩老师打台球,你一定不会先去台球厅,而是会先打电话联系,问他有没有时间一起打球。同理,如果在编程时需要处理一个对象,也应先对对象进行测试,只有在确定它符合要求时,才应进行操作处理,这样做的好处就是避免程序出错及无谓的系统资源消耗,这个需要测试的对象可以是文件、字符串、数字等。

在书写文件测试表达式时,通常可以使用表中的文件测试操作符。

上表列出的是企业里比较常用的操作符,这些操作符号对于​\[​​]​、​​、test的测试表达式几乎是通用的,更多的操作符请通过man test获得帮助。

2.2、文件测试表达式举例

2.2.1、普通文件测试表达式示例
  • (1)普通文件(测试文件类型)

    bash 复制代码
    [root@oldboy ~]# touch oldboy  #<==创建文件oldboy。
    [root@oldboy ~]# ls -l oldboy
    -rw-r--r-- 1 root root 0 Aug   9 22:40 oldboy
    [root@oldboy ~]# [ -f oldboy ] && echo 1 || echo 0 #<==测试文件是否存在,如果存在,则输出1,否则输出0
    1  #<==因为刚刚创建了oldboy文件,因此条件测试表达式成立,输出1。
  • (2)目录文件(测试文件类型)

bash 复制代码
[root@oldboy ~]# mkdir oldgirl  #<==创建目录oldgirl。
[root@oldboy ~]# [ -f oldgirl ] && echo 1 || echo 0 #<==测试oldgirl是否为普通文件。
0  #<==输出为0,证明oldgirl非普通文件,因为前面创建的是oldgirl目录,因此,输出0是对的。
[root@oldboy ~]# [ -e oldgirl ] && echo 1 || echo 0 #<==测试oldgirl是否存在。
1  #<==只要oldgirl存在就行,不管是目录还是普通文件,oldgirl确实存在,因此输出1。
[root@oldboy ~]# [ -d oldgirl ] && echo 1 || echo 0 #<==测试oldgirl是否为目录。
1  #<==输出1,证明oldgirl是目录。
[root@oldboy ~]# [ -d oldboy ] && echo 1 || echo 0 #<==测试oldboy是否为目录。
0  #<==输出0,证明oldboy非目录。
2.2.2、测试文件属性示例
bash 复制代码
[root@oldboy ~]# ls -l oldboy
-rw-r--r-- 1 root root 0 Aug   9 22:40 oldboy
#<==文件权限默认为644,权限基础可参考老男孩基础类图书或视频讲解。
[root@oldboy ~]# [ -r oldboy ] && echo 1 || echo 0  #<==测试oldboy是否可读。
1  #<==输出为1,因为用户权限位有r,因此,可以读取oldboy。
[root@oldboy ~]# [ -w oldboy ] && echo 1 || echo 0  #<==测试oldboy是否可写。
1  #<==输出为1,因为用户权限位有w,因此,可以写入oldboy。
[root@oldboy ~]# [ -x oldboy ] && echo 1 || echo 0  #<==测试oldboy是否可执行。
0  #<==输出为0,因为用户权限位没有x,因此,不可以执行oldboy。
[root@oldboy ~]# chmod 001 oldboy  #<==修改oldboy的权限位001。
[root@oldboy ~]# ls -l oldboy
---------x 1 root root 0 Aug  9 22:40 oldboy #<==修改后的结果。
[root@oldboy ~]# [ -w oldboy ] && echo 1 || echo 0
1  #<==用户权限位明明没有w,为什么还是返回1呢?
[root@oldboy ~]# echo 'echo test' >oldboy  #<==因为确实可以写啊,这是root用户比较特殊的地方。
[root@oldboy ~]# [ -r oldboy ] && echo 1 || echo 0
1  #<==用户权限位明明没有r,为什么还是返回1呢?
[root@oldboy ~]# cat oldboy
echo test  #<==因为确实可以读啊,这是root用户比较特殊的地方。
[root@oldboy ~]# [ -x oldboy ] && echo 1 || echo 0
1
[root@oldboy ~]# ./oldboy    #<==可执行。
test

提示:测试文件的读、写、执行等属性,不光是根据文件属性rwx的标识来判断,还要看当前执行测试的用户是否真的可以按照对应的权限操作该文件。

2.2.3、测试Shell变量示例

首先定义file1和file2两个变量,并分别赋予这两个变量对应的系统文件路径及文件名的值,如下:

bash 复制代码
[root@oldboy ~]# file1=/etc/services; file2=/etc/rc.local  #<==分号用于分隔两个命令。
[root@oldboy ~]# echo $file1 $file2
/etc/services /etc/rc.local

范例:对单个文件变量进行测试。

bash 复制代码
[root@oldboy ~]# [ -f "$file1" ] && echo 1 || echo 0    #<==文件存在且为普通文件,所以为真(1)。
1
[root@oldboy ~]# [ -d "$file1" ] && echo 1 || echo 0    #<==是文件而不是目录,所以为假(0)。
0
[root@oldboy ~]# [ -s "$file1" ] && echo 1 || echo 0    #<==文件存在且大小不为0,所以为真(1)。
1
[root@oldboy ~]# [ -e "$file1" ] && echo 1 || echo 0    #<==文件存在,所以为真(1)。
1

范例:对单个目录或文件进行测试。

bash 复制代码
[root@oldboy ~]# [ -e /etc ] && echo 1 || echo 0
1
[root@oldboy ~]# [ -w /etc/services ] && echo 1 || echo 0
1
[root@oldboy ~]# su - oldboy  #<==切换到普通用户。
[oldboy@oldboy ~]$ [ -w /etc/services ] && echo 1 || echo 0 #<==文件不可写,所以返回0。
0

范例:测试时变量的特殊写法及问题。

测试变量时,如果被测试的变量不加双引号,那么测试结果可能会是不正确的,示例如下:

bash 复制代码
[root@oldboy ~]# echo $oldgirl    #<==这是一个不存在的变量,如果读者已经定义,则可以执行unset oldgirl取消。
[root@oldboy ~]# [ -f $oldgirl ] && echo 1 || echo 0    #<==不加引号测试变量。
1 #<==明明$oldgirl变量不存在内容还返回1,逻辑就不对了
[root@oldboy ~]# [ -f "$oldgirl" ] && echo 1 || echo 0 #<==加引号测试变量。
0 #<==加了双引号就返回0,逻辑就对了。

如果是文件实体路径,那么加引号与不加引号的结果是一样的:

bash 复制代码
[root@oldboy ~]# [ -f "/etc/services" ] && echo 1 || echo 0 #<==加引号测试文件。
1
[root@oldboy ~]# [ -f /etc/services ] && echo 1 || echo 0 #<==不加引号测试文件。
1

范例:在生产环境下,系统NFS启动脚本的条件测试。

bash 复制代码
[root@oldboy ~]# more /etc/init.d/nfs
# Source networking configuration.
[ -f /etc/sysconfig/network ] && . /etc/sysconfig/network
#<==如果/etc/sysconfig/network文件存在,则加载文件。
# Check for and source configuration file otherwise set defaults
[ -f /etc/sysconfig/nfs ] && . /etc/sysconfig/nfs
#<==如果/etc/sysconfig/nfs文件存在,则加载文件。

范例:实现系统bind启动脚本named(bind DNS服务)​。

bash 复制代码
[ -r /etc/sysconfig/network ] && . /etc/sysconfig/network
#<==若文件存在且可读,则加载/etc/sysconfig/network。
[ -x /usr/sbin/$named ] || exit 5 #<==如果/usr/sbin/$named不可执行,则退出。

特别提示:前面所讲的都是 -f/etc && echo 1 ||echo 0的用法,bind启动脚本 -x /usr/sbin/$named || exit 5的用法更值得注意,这里只用了一部分判断,结果却更简洁。

范例:写出简单高效的测试文件。

在做测试判断时,不一定非要按照"前面的操作成功了如何,否则如何"的方法来进行。直接做部分判断,有时看起来更简洁。例如:

bash 复制代码
[root@oldboy ~]# [ -x oldboy ] && echo 1
#<==如果oldboy可执行,则输出1;如果不可执行,则不做任何输出。
1
[root@oldboy ~]# [ -f /etc ] || echo 0
#<==如果/etc是文件这一点不成立,则输出0;如果成立,则不做任何输出。
0

范例:实现系统脚本/etc/init.d/nfs。

bash 复制代码
[root@oldboy ~]# sed -n '44,50p' /etc/init.d/nfs  #<==查看NFS脚本的第44~50行。
          # Check that networking is up.
          [ "${NETWORKING}" ! = "yes" ] && exit 6 #<==如果${NETWORKING}的变量内容不等于yes,则退出。
          [ -x /usr/sbin/rpc.nfsd ] || exit 5
          #<== 如果/usr/sbin/rpc.nfsd的脚本不可执行,则以返回值5退出脚本。如果可执行,则不做任何输出。
          [ -x /usr/sbin/rpc.mountd ] || exit 5
          [ -x /usr/sbin/exportfs ] || exit 5

2.3、特殊条件测试表达式案例

以下写法适用于所有的条件测试表达式,是工作中比较常用的替代if语句的方法。判断条件测试表达式的条件成立或不成立后,还需要继续执行多条命令语句的语法形式如下。

例如:当条件1成立时,同时执行命令1、命令2、命令3。不用if测试表达式的格式如下:

bash 复制代码
[ 条件1 ] &&{
    命令1
    命令2
    命令3
}
[[ 条件1 ]] &&{
    命令1
    命令2
    命令3
}
test 条件1 &&{
    命令1
    命令2
    命令3
}

上面的判断相当于下面if语句的效果。

bash 复制代码
if [ 条件1 ]
  then
    命令1
    命令2
    命令3
fi

范例:当条件不成立时,执行大括号里的多条命令,这里要使用逻辑操作符"||"​。

bash 复制代码
[root@oldboy scripts]# cat 6_15.sh
[ -f /etc ] || {        #<==如果/etc/是普通文件不成立,则执行大括号里的命令集合,这显然不成立啊!
   echo 1
   echo 2
   echo 3
}
[root@oldboy scripts]# sh 6_15.sh  #<==/etc/是普通文件显然是不成立的,因此会执行大括号里的命令集!
1
2
3

如果把上述脚本写在一行里面,那么里面的每个命令都需要用分号结尾,示例如下所示:

bash 复制代码
[root@oldboy ~]# [ -f /etc/services ] && { echo "I am oldboy"; echo "I am linuxer"; }
I am oldboy
I am linuxer

提示:本例的两种用法都很简洁,但是不如if条件语句容易理解,因此,请读者根据自身情况选择使用,更多帮助请通过man test查询。

3、字符串测试表达式

3.1、字符串测试操作符

字符串测试操作符的作用包括:比较两个字符串是否相同、测试字符串的长度是否为零、字符串是否为NULL等。

在书写测试表达式时,可以使用表中的字符串测试操作符。

以下是针对字符串测试操作符的提示:

  • 对于字符串的测试,一定要将字符串加双引号之后再进行比较。如 -n "$myvar" ,特别是使用\[\]的场景。
  • 比较符号(例如=和!=)的两端一定要有空格。
  • "! ="和"="可用于比较两个字符串是否相同。

范例:字符串条件表达式测试实践。

bash 复制代码
[root@oldboy ~]# [ -n "abc" ] && echo 1 || echo 0  #<==如果字符串长度不为0,则输出1,否则输出0。
1 #<==因为字符串为abc,长度不为0,因此为真,输出1。
[root@oldboy ~]# test -n "abc" && echo 1 || echo 0 #<==test的用法同上述[]的用法。
1
[root@oldboy ~]# test -n "" && echo 1 || echo 0
0
[root@oldboy ~]# var="oldboy" #<==给变量var赋值oldboy字符串。
[root@oldboy ~]# [ -n "$var" ] && echo 1 || echo 0    #<==如果字符串长度不为0,则输出1,否则输出0。
1 #<==因为变量var内容字符串为oldboy,长度不为0,因此为真,输出1。
[root@oldboy ~]# [ -n $var ] && echo 1 || echo 0 #<==去掉双引号在这里看起来也是对的,不过还是加上为好。
1
[root@oldboy ~]# var="oldgirl"
[root@oldboy ~]# [ -z "$var" ] && echo 1 || echo 0  #<==使用-z,变量长度为0,则为真。
0    #<==变量var的值为oldgirl,长度不为0,所以表达式不成立,输出0。
[root@oldboy ~]# [ "abc" = "abc" ] && echo 1 || echo 0
#<==字符串相等,输出1,注意"="两端要有空格。
1
[root@oldboy ~]# [ "abc" = "abd" ] && echo 1 || echo 0
#<==字符串不相等,输出0,注意"="两端要有空格。
0
[root@oldboy ~]# [ "$var" = "oldgirl" ] && echo 1 || echo 0
#<==变量值和字符串相等,输出1。
1
[root@oldboy ~]# [ "$var" == "oldgirl" ] && echo 1 || echo 0
#<==使用"=="代替"=",注意"="两端要有空格。
1
[root@oldboy ~]# [   "$var" ! = "oldgirl" ] && echo 1 || echo 0
0

范例:进行字符串比较时,等号两端没有空格带来的问题。

bash 复制代码
[root@oldboy ~]# [ "abc"="1" ] && echo 1||echo 0   #<==若等号两端不带空格,则会出现明显的逻辑错误。
1  #<==明明表达式不成立,却输出了1。
[root@oldboy ~]# [ "abc" = "1" ] && echo 1||echo 0 #<==带空格的就是准确的。
0  #<==表达式不成立,输出0。

结论:字符串比较时若等号两端没有空格,则会导致判断出现逻辑错误,即使语法没问题,但是结果依然可能不对。

范例:字符串不加引号可能带来的问题。

bash 复制代码
[root@oldboy ~]# var="" #<==将变量内容置为空。
[root@oldboy ~]# [ -n "$var" ] && echo 1 || echo 0   #<==有双引号。
0 #<==给变量加双引号,返回0, -n不为空时为真,因为变量内容为空,因此输出0是对的。
[root@oldboy ~]# [ -n $var ] && echo 1 || echo 0     #<==去掉双引号。
1 #<==同样的表达式,不加引号和加双引号后测试的结果相反,可见加双引号的重要性。
[root@oldboy ~]# [ -z "$var" ] && echo 1 || echo 0 #<==如果字符串长度为0,则输出1,否则输出0。
1

结论:字符串不加双引号,可能会导致判断上出现逻辑错误,即使语法没问题,但是结果依然可能不对。

3.2、字符串测试生产案例

范例:有关双引号和等号两端空格的生产系统标准。

bash 复制代码
[root@oldboy ~]# sed -n '30,31p' /etc/init.d/network  #<==系统网卡启动脚本案例。
# Check that networking is up.
[ "${NETWORKING}" = "no" ] && exit 6    #<==字符串变量和字符串都加了双引号,比较符号"="两端也都有空格。

范例:系统脚本/etc/init.d/nfs字符串测试的应用示例。

bash 复制代码
[root@oldboy ~]# sed -n '65,80p' /etc/init.d/nfs
        [ -z "$MOUNTD_NFS_V2" ] && MOUNTD_NFS_V2=default
#<==-z的应用,如果变量MOUNTD_NFS_V2的长度为0则赋值default。
        [ -z "$MOUNTD_NFS_V3" ] && MOUNTD_NFS_V3=default
#<==-z的应用,如果变量MOUNTD_NFS_V2的长度为0则赋值default。
        # Number of servers to be started by default
        [ -z "$RPCNFSDCOUNT" ] && RPCNFSDCOUNT=8 #<==-z的应用。
        # Start daemons.
        [ -x /usr/sbin/rpc.svcgssd ] && /sbin/service rpcsvcgssd start
        # Set the ports lockd should listen on
        if [ -n "$LOCKD_TCPPORT" -o -n "$LOCKD_UDPPORT" ]; then  #<==-n的应用。
                  [ -x /sbin/modprobe ] && /sbin/modprobe lockd $LOCKDARG
                  [ -n "$LOCKD_TCPPORT" ] && \
                          /sbin/sysctl -w fs.nfs.nlm_tcpport=$LOCKD_TCPPORT >/dev/
null 2>&1
                  [ -n "$LOCKD_UDPPORT" ] && \
                          /sbin/sysctl -w fs.nfs.nlm_udpport=$LOCKD_UDPPORT >/
dev/null 2>&1

4、整数二元比较操作符

4.1、整数二元比较操作符介绍

在书写测试表达式时,可以使用表中的整数二元比较操作符。

以下是针对上述符号的特别说明:

  • "="和"! ="也可在\[\]中做比较使用,但在\[\]中使用包含">"和"<"的符号时,需要用反斜线转义,有时不转义虽然语法不会报错,但是结果可能会不对。
  • 也可以在\[]中使用包含"-gt"和"-lt"的符号,但是不建议这样使用。
  • 比较符号两端也要有空格。

范例:二元数字在中使用"<"​、​">"非标准符号的比较。

bash 复制代码
[root@oldboy ~]# [ 2 > 1 ] && echo 1 || echo 0
1
[root@oldboy ~]# [ 2 < 1 ] && echo 1 || echo 0
1  #<==这里的结果逻辑不对,条件不成立,则应该返回0,可见,"<"操作符在[]里使用时会带来问题。
[root@oldboy ~]# [ 2 \< 1 ] && echo 1 || echo 0
0 #<==转义后这里是正确的。
[root@oldboy ~]# [ 2 = 1 ] && echo 1 || echo 0    #<==比较相等符号是正确的。
0
[root@oldboy ~]# [ 2 = 2 ] && echo 1 || echo 0    #<==比较相等符号是正确的。
1
[root@oldboy ~]# [ 2 ! = 2 ] && echo 1 || echo 0  #<==比较不相等符号也是正确的。
0

对于比较符号的应用,建议读者尽可能地按照表6-4中标记的方法来使用,以避免出现逻辑错误。

范例:二元数字在中使用-gt、-le类符号的比较。

bash 复制代码
[root@oldboy ~]# [ 2 -gt 1 ] && echo 1 || echo 0
1  #<==2大于1成立,输出1。
[root@oldboy ~]# [ 2 -ge 1 ] && echo 1 || echo 0
1  #<==2大于等于1成立,输出1。
[root@oldboy ~]# [ 2 -le 1 ] && echo 1 || echo 0
0  #<==2小于等于1不成立,输出0。
[root@oldboy ~]# [ 2 -lt 1 ] && echo 1 || echo 0
0  #<==2小于1不成立,输出0。

范例:二元数字配合不同种类的操作符在​\[​​]中的比较。

bash 复制代码
[root@oldboy ~]# [[ 5 > 6 ]] && echo 1 || echo 0
0  #<==5大于6不成立,输出0。
[root@oldboy ~]# [[ 5 < 6 ]] && echo 1 || echo 0
1  #<==5小于6成立,输出1。
[root@oldboy ~]# [[ 5 ! = 6 ]] && echo 1 || echo 0
1  #<==5不等于6成立,输出1。
[root@oldboy ~]# [[ 5 = 6 ]] && echo 1 || echo 0
0  #<==5等于6不成立,输出0。
[root@oldboy ~]# [[ 5 -gt 6 ]] && echo 1 || echo 0
0  #<==5大于6不成立,输出0。
[root@oldboy ~]# [[ 5 -lt 6 ]] && echo 1 || echo 0
1  #<==5小于6成立,输出1。
[root@oldboy ~]# [[ 65 > 66 ]] && echo 1 || echo 0
0  #<==65大于66不成立,输出0。
[root@oldboy ~]# [[ 65 < 66 ]] && echo 1 || echo 0
1  #<==65小于66成立,输出1。
[root@oldboy ~]# [[ 65 = 66 ]] && echo 1 || echo 0
0  #<==65等于66不成立,输出0。

提示:​​\[​​]是扩展的test命令,其语法更丰富也更复杂。对于实际工作中的常规比较,不建议使用​\[​​]​,会给Shell学习带来很多麻烦,除非是特殊的正则匹配等,在无法使用的场景下才会考虑使用​\[​​]​。

范例:二元数字在(())中的比较。

bash 复制代码
[root@oldboy ~]# ((3>2)) && echo 1 || echo 0
1  #<==3大于2成立,输出1。
[root@oldboy ~]# ((3<2)) && echo 1 || echo 0
0  #<==3小于2不成立,输出0。
[root@oldboy ~]# ((3==2)) && echo 1 || echo 0
0  #<==3等于2不成立,输出0。
[root@oldboy ~]# ((3! ==2)) && echo 1 || echo 0 #<=="! =="符号不可用,语法错误
-bash: ((: 3! ==2: syntax error: operand expected (error token is "=2")
0
[root@oldboy ~]# ((3! =2))&& echo 1 || echo 0
1

有关​、​​\[​​]​、(())用法的小结:

  • 整数加双引号的比较是对的。
  • \[]中用类似-eq等的写法是对的,\[]中用类似>、<的写法也可能不对,有可能会只比较第一位,逻辑结果不对。
  • \[\]中用类似>、<的写法在语法上虽然可能没错,但逻辑结果不对,可以使用=、! =正确比较。
  • (())中不能使用类似-eq等的写法,可以使用类似>、<的写法。

提示:对于工作场景中的整数比较,推荐使用​(类似-eq的用法)​,这是本书作者的习惯,当然使用(())的写法也是可以的。

4.2、整数变量测试实践示例

范例:通过实现整数条件测试。

bash 复制代码
[root@oldboy ~]# a1=98; a2=99
[root@oldboy ~]# [ $a1 -eq $a2 ] && echo 1 || echo 0 #<==测试$a1是否等于$a2。
0
[root@oldboy ~]# [ $a1 -gt $a2 ] && echo 1 || echo 0 #<==测试$a1是否大于$a2。
0
[root@oldboy ~]# [ $a1 -lt $a2 ] && echo 1 || echo 0 #<==测试$a1是否小于$a2。
1

提示:有关整数(要确认是整数,否则会报错)大小的比较,推荐使用本例中的方法。

范例:利用​\[​​]和(())实现直接通过常规数学运算符进行比较。

bash 复制代码
[root@oldboy ~]# [[ $a1 > $a2 ]] && echo 1 || echo 0  #<==测试$a1是否大于$a2,尽量不用此写法。
0
[root@oldboy ~]# [[ $a1 < $a2 ]] && echo 1 || echo 0  #<==测试$a1是否小于$a2,尽量不用此写法。
1
[root@oldboy ~]# (($a1>=$a2)) && echo 1 || echo 0   #<==测试$a1是否大于等于$a2,此写法也可以。
0
[root@oldboy ~]# (($a1<=$a2)) && echo 1 || echo 0   #<==测试$a1是否小于等于$a2,此写法也可以。
1

有关整数(要确认是整数,否则会报错)的大小比较,(())语法要优于​\[​​]​,但还是推荐优先使用​,次选是(()),不推荐使用​\[​​]​。示例如下:

bash 复制代码
[ $num1 -eq $num2 ]      #<==注意比较符号两边的空格和比较符号的写法,必须要有空格。
(($num1>$num2))           #<==比较符号两边无需空格(多空格也可),使用常规数学的比较符号即可。

范例:系统脚本中使用整数比较的案例。

bash 复制代码
[root@oldboy ~]# grep -w "\-eq" /etc/init.d/nfs #<==过滤出相等(-eq)的例子。
       [ $RETVAL -eq 0 ] && RETVAL=$rval         #<==使用[],且两边都要有一个空格。
                 [ $RETVAL -eq 0 ] && RETVAL=$rval #<==使用"-eq"的比较操作符的写法。
                 [ $RETVAL -eq 0 ] && RETVAL=$rval
       [ $RETVAL -eq 0 ] && RETVAL=$rval
                 [ $RETVAL -eq 0 ] && RETVAL=$rval
[root@oldboy ~]# grep -w "\-gt" /etc/init.d/nfs    #<==过滤出大于(-gt)的例子。
       if [ $cnt -gt 0 ]; then

5、逻辑操作符

5.1、逻辑操作符介绍

在书写测试表达式时,可以使用表中的逻辑操作符实现复杂的条件测试。

对于上述操作符,有如下提示:

  • 逻辑操作符前后的表达式是否成立,一般用真假来表示。
  • "! "的中文意思是反,即与一个逻辑值相反的逻辑值。
  • -o的中文意思是"或"(or或||),前后两个逻辑值只要有一个为"真",返回值就为"真"。
  • 连接两含 、test或\[ ]的表达式可用&&或||。

逻辑操作符运算规则

  • -a和&&的运算规则:只有逻辑操作符两端的表达式都成立时才为真;真(true)表示成立,对应的数字为1;假(false)表示不成立,对应的数字为0,这一点相当于如下表达式:

    bash 复制代码
    [root@oldboy ~]# [ -f /etc/hosts -a -f /etc/services ] && echo 1 || echo 0
    #<==单中括号文件测试。
    1
    [root@oldboy ~]# [[ -f /etc/hosts && -f /etc/services ]] && echo 1 || echo 0
    #<==双中括号文件测试。
    1

    使用-a和&&的综合表达式结果,相当于将两端表达式结果的对应数字(0或1)相乘。

    • 当左边为真,右边为假的时候,相乘结果为1*0=0,总结果为假(0)。

    • 当左边为假,右边为真的时候,相乘结果为0*1=0,总结果依然为假(0)。

    • 当左边为真,右边也为真的时候,相乘结果为1*1=1,总结果为真(1)。

    • 当左边为假,右边也为假的时候,相乘结果为0*0=0,总结果为假(0)。
      简单表示为:

      bash 复制代码
      and结果1*0=0 假
      and结果0*1=0 假
      and结果1*1=1 真
      and结果0*0=0 假

    结论:and(&&)也称为与,只有两端都是1时才为真,相当于取前后表达式的交集。

    -o或||两端都是0才为假,任何一端不为0就是真,这相当于将两边表达式结果的对应数字(0或1)相加,对应的表达式为:

    bash 复制代码
    [root@oldboy ~]# [ 5 -eq 6 -o 5 -gt 3 ] && echo 1 || echo 0
    1
    [root@oldboy ~]# ((5==6||5>3)) && echo 1 || echo 0
    1
  • -o或||的运算规则为:

    • 当左边为真,右边为假的时候,相加结果为1+0=1,总结果为真(1)。

    • 当左边为假,右边为真的时候,相加结果为0+1=1,总结果依然为真(1)。

    • 当左边为真,右边也为真的时候,相加结果为1+1=2,总结果为真(1),非0即真。

    • 当左边为假,右边也为假的时候,相加结果为0+0=0,总结果为假(0)。

      bash 复制代码
      or 结果1+0=1 真
      or 结果1+1=2 真(非0即为真)
      or 结果0+1=1 真
      or 结果0+0=0 假

    结论:or(||)也称为或,它的两端表达式的结果都是0时才为假,不为0就是真。相当于对前后表达式结果取并集。

5.2、逻辑操作符实践示例

范例:里的逻辑操作符配合文件测试表达式使用的示例。

bash 复制代码
[root@oldboy ~]# f1=/etc/rc.local; f2=/etc/services
#<==定义f1和f2两个变量,分别赋值两个已知存在的文件路径。
[root@oldboy ~]# echo -ne "$f1 $f2\n"  #<==测试输出f1和f2两个变量的值。
/etc/rc.local /etc/services
[root@oldboy ~]# [ -f "$f1" -a -f "$f2" ] && echo 1 || echo 0 #<==[]里使用-a(and)。
#<==测试f1和f2两个变量值是否都为普通文件,如果成立,则输出1,否则输出0。
1
[root@oldboy ~]# [ -f "$f1" -o -f "$f222" ] && echo 1 || echo 0
#<==测试f1和f222两个变量值是否都为普通文件,如果成立,则输出1,否则输出0; f222变量是未定义的,但是f1变量是有的,并且是普通文件,-o两边有一个表达式成立(即为真),因此输出1。
1
[root@oldboy ~]# [ -f "$f111" -o -f "$f222" ] && echo 1 || echo 0
#<==测试f111和f222两个变量值中是否有一个为普通文件,如果成立,则输出1,否则输出0。
0 #<==f111和f222两个变量都是未定义的,因此输出0。
[root@oldboy ~]# [ -f "$f1" && -f "$f2" ] && echo 1 || echo 0 #<==这是错误语法,
[]中不能用&&或||。
-bash: [: missing `]'
0
[root@oldboy ~]# [ -f "$f1" ] && [ -f "$f2" ] && echo 1 || echo 0
#<==如果在[]中想使用&&,则这样用。
1

范例:​\[​​]里逻辑操作符配合字符串的条件表达式的测试示例。

bash 复制代码
[root@oldboy ~]# a="oldboy"; b="oldgirl"  #<==定义a和b两个变量,赋值两个已知的字符串。
[root@oldboy ~]# echo -ne "$a $b\n"  #<==测试输出a和b两个变量的值。
oldboy oldgirl
[root@oldboy ~]# [[ ! -n "$a" && "$a" = "$b" ]] && echo 1 || echo 0
#<== [[]]内部用&&或||。
#<==测试$a长度不为0是否成立,并且$a等于$b是否成立,两个表达式是否同时成立。
0 #<==因为$a长度不为0成立,结果为1,加上前面"! "取反,就是不成立,结果变为0。$a等于$b成立,结果为1,两个表达式综合起来0*1=0,因此不成立,输出0。
[root@oldboy ~]# [[ -z "$a" || "$a" ! = "$b" ]] && echo 1 || echo 0
#<== [[]]中||的使用。
1
[root@oldboy ~]# [[ -z "$a" -o "$a" ! = "$b" ]] && echo 1 || echo 0
#<== [[]]内部用-a或-o会报错。
-bash: syntax error in conditional expression
-bash: syntax error near `-o'

范例:(())里逻辑操作符配合整数的条件表达式测试示例。

bash 复制代码
[root@oldboy ~]# m=21; n=38
[root@oldboy ~]# ((m>20&&n>30)) && echo 1 || echo 0 #<== (())内部用&&或||。
1
[root@oldboy ~]# ((m<20||n>30)) && echo 1 || echo 0
1
[root@oldboy ~]# ((m<20||n<30)) && echo 1 || echo 0
0
[root@oldboy ~]# ((m<20 -a n<30)) && echo 1 || echo 0
#<== (())内部用-a或-o也会报语法错误。
-bash: ((: m<20 -a n<30: syntax error in expression (error token is
"n<30")
0

范例:使用多个号,并通过与或非进行混合测试。

bash 复制代码
[root@oldboy ~]# m=21; n=38
[root@oldboy ~]# [ $m -gt 20 -a $n -lt 30 ] && echo 1 || echo 0
0
[root@oldboy ~]# [ $m -gt 20 ]||[ $n -lt 30 ] && echo 1 || echo 0
#<==多个[]号之间用&&或||连接。
1

范例:NFS系统启动脚本中有关与或非判断的使用案例。

bash 复制代码
[root@oldboy ~]# egrep -wn "\-a|\-o" /etc/init.d/nfs
75:      if [ -n "$LOCKD_TCPPORT" -o -n "$LOCKD_UDPPORT" ]; then  #<==[]-o的用法
87:      [ "$NFSD_MODULE" ! = "noload" -a -x /sbin/modprobe ] && { #<==[]-a的用法
102:     if [ -n "$RQUOTAD" -a "$RQUOTAD" ! = "no" ]; then  #<==[]-a的用法
170:     if [ -n "$RQUOTAD" -a "$RQUOTAD" ! = "no" ]; then  #<==[]-a的用法
209:     if [ -n "$RQUOTAD" -a "$RQUOTAD" ! = "no" ]; then  #<==[]-a的用法
229:     if [ $MOUNTD = 1 -o $NFSD = 1 ] ; then

提示:可见中使用-a或-o更常见,​​\[​​]中使用&&或||不常见,使用&&或||连接两个的多表达式判断也不常见。

范例:系统启动脚本中有关​\[​​]的用法和与或非判断的使用案例。

在操作系统中,​​\[​​]的用法不是很多,并且大多数情况都用于与通配符匹配的场景。

这里不得不通过大海捞针的方法(遍历/etc/init.d/下的所有脚本)来帮助大家查找​\[​​]的用法:

bash 复制代码
[root@oldboy ~]# for n in `ls /etc/init.d/*`; do egrep -wn "\[\[ " $n&&echo
$n; done
119:  if [[ "$dst" == /dev/mapper* ]] \  #<==[[]]通配符匹配的场景,此方法不支持[]。
/etc/init.d/halt
68:    if [[ $? = 0 ]]; then #<==[[]]的常规场景,此方法支持[]。
/etc/init.d/httpd
561:  if [[ -n "$_target" ]]; then #<==[[]]的常规场景,此方法支持[]。
576:   if [[ "$_rmnt" == "$_mnt" ]] || ! is_dump_target_configured; then
/etc/init.d/kdump
50:    if [[ $route == *" via "* ]] ; then #<==[[]]通配符匹配的场景,此方法不支持[]。
71:    if ! [[ "$SYSLOGADDR" =~ $MATCH ]]; then #<==[[]]通配符匹配的场景,此方法不支持[]。
/etc/init.d/netconsole
162:   if [[ "$rootfs" == nfs* || "$rootopts" =~ _r? netdev ]] ; then
#<==[[]]通配符匹配的场景,此方法不支持[]。
/etc/init.d/network

由上述遍历可见,​​\[​​]的普通应用场景不多,但在​\[​​]通配符匹配的场景下,其他测试表达式无法替代,因此,如果需要通配符或正则匹配就用​\[​​]​。

5.3、逻辑操作符企业案例

范例:输入或通过命令行传入一个字符或数字,如果传入的数字等于1,就打印1;如果等于2,就打印2;如果不等于1也不能于2,就提示输入不对,然后退出程序。

参考答案1:使用read读入内容方案。

bash 复制代码
[root@oldboy scripts]# cat 6_34_1.sh
#! /bin/sh
echo -n "pls input a char:" #<==打印提示字符串,-n表示不换行。
read var #<==读取用户的输入并赋值给var变量。
[ "$var" == "1" ] &&{ #<==条件表达式判断,看变量是否等于1,注意普通字符比较多地用字符串比较的语法,即加双引号比较,而不是使用整数比较的语法,整数比较容易出错,除非确定是整数。
    echo 1
    exit 0 #<==每个逻辑正确,则以返回值0退出脚本,从而避免执行脚本后面无用的代码。
}
[ "$var" == "2" ] &&{ #<==条件表达式判断,看变量是否等于2。
    echo 2
    exit 0
}
[ "$var" ! = "2" -a "$var" ! = "1" ] &&{  #<==条件表达式加逻辑操作符判断,看变量是否不等于2,并且不等于1,如果都成立,则执行命令。
    echo error
    exit 0
}

执行结果如下:

bash 复制代码
[root@oldboy scripts]# sh 6_34_2.sh
pls input a char:1
1
[root@oldboy scripts]# sh 6_34_1.sh
pls input a char:2
2
[root@oldboy scripts]# sh 6_34_1.sh
pls input a char:4
error

参考答案2:使用脚本命令行传参读入内容的解决方案。

bash 复制代码
[root@oldboy scripts]# cat 6_34_2.sh
#! /bin/sh
var=$1 #<==将上文的read读入信息,改为脚本传参,接收脚本的第一个参数$1赋值给var。
[ "$var" == "1" ] &&{
    echo 1
    exit 0
}
[ "$var" == "2" ] &&{
    echo 2
    exit 0
}
[ "$var" ! = "2" -a "$var" ! = "1" ] &&{
    echo error
    exit 0
}

范例:开发Shell脚本,分别实现以脚本传参和read读入的方式比较两个整数的大小。用条件表达式(禁止用if)进行判断并以屏幕输出的方式提醒用户比较的结果。注意:一共是开发两个脚本。在用脚本传参和read读入的方式实现时,需要对变量是否为数字及传参个数是否正确给予提示。

参考答案1:采用read方法。

bash 复制代码
[root@oldboy scripts]# cat 6_35_1.sh
#! /bin/sh
read -p "Pls input two num:" a b  #<==请求用户输入两个参数,读入后分别赋值给变量a和b。
#no1
[ -z "$a" ]||[ -z "$b" ] &&{    #<==如果$a变量长度为0或$b变量长度为0,即任何一个变量为空,则执行命令。
    echo "Pls input two num again." #<==a或b没值,表示用户输入错误,给出提示。
    exit 1 #<==以返回值1退出脚本。
}
#no2
expr $a + 10 &>/dev/null #<==判断$a是否为整数,不输出任何信息。前文已讲解过了expr判断整数的方式。
RETVAL1=$? #<==将返回值赋值给RETVAL1,后面会用这个返回值做判断。
expr $b + 10 &>/dev/null #<==判断$b是否为整数,不输出任何信息。
RETVAL2=$? #<==将返回值赋值给RETVAL2,后面会用这个返回值做判断。
test $RETVAL1 -eq 0 -a $RETVAL2 -eq 0||{ #<==利用test进行返回值是否为0的判断。如果有一个返回值不为0,则说明有一个变量不为整数,不合要求,打印提示后退出。
    echo "Pls input two "num" again."
    exit 2
}
#no3
[ $a -lt $b ] &&{  #<==整数比较,$a是否小于$b。
    echo "$a < $b"
    exit 0
}
#no4
[ $a -eq $b ] &&{  #<==整数比较,$a是否等于$b。
    echo "$a = $b"
    exit 0
}
#no5
[ $a -gt $b ] &&{  #<==整数比较,$a是否大于$b。
    echo "$a > $b"
}

执行结果如下:

bash 复制代码
[root@oldboy scripts]# sh 6_35_1.sh
Pls input two num:6 2     #<==常规正确输入6和2。
6 > 2
[root@oldboy scripts]# sh 6_35_1.sh
Pls input two num:6 6     #<==常规正确输入6和6。
6 = 6
[root@oldboy scripts]# sh 6_35_1.sh
Pls input two num:2 6     #<==常规正确输入2和6。
2 < 6
[root@oldboy scripts]# sh 6_35_1.sh
Pls input two num:        #<==非常规错误输入,直接回车不输入。
Pls input two num again. #<==给出提示,退出脚本。
[root@oldboy scripts]# sh 6_35_1.sh
Pls input two num:dd ff   #<==非常规错误输入,输入非数字。
Pls input two num again. #<==给出提示,退出脚本。

参考答案2:通过命令行传参的方法实现。

bash 复制代码
[root@oldboy scripts]# cat 6_35_2.sh
#! /bin/sh
a=$1 #<==将上文的read读入信息改为脚本传参,接收脚本的第一个参数$1赋值给a。
b=$2 #<==将上文的read读入信息改为脚本传参,接收脚本的第二个参数$2赋值给b。
#no1
[ $# -ne 2 ] &&{ #<==传参专用判断手法,判断传参个数是否为两个。
    echo "USAGE:$0 NUM1 NUM2" #<==传参不合要求,给出用法提示。
    exit 1 #<==以返回值1退出脚本。
}
#no2
expr $a + 10 &>/dev/null     #<==整数判断,前文已讲。
RETVAL1=$?
expr $b + 10 &>/dev/null     #<==整数判断,前文已讲。
RETVAL2=$?
test $RETVAL1 -eq 0 -a $RETVAL2 -eq 0||{
    echo "Pls input two "num" again."
    exit 2
}
#no3
[ $a -lt $b ] &&{
    echo "$a < $b"
    exit 0
}
#no4
[ $a -eq $b ] &&{
    echo "$a = $b"
    exit 0
}
#no5
[ $a -gt $b ] &&{
    echo "$a > $b"
}

执行结果如下:

bash 复制代码
[root@oldboy scripts]# sh 6_35_2.sh
USAGE:6_34_2.sh NUM1 NUM2
[root@oldboy scripts]# sh 6_35_2.sh 6 2
6 > 2
[root@oldboy scripts]# sh 6_35_2.sh 6 6
6 = 6
[root@oldboy scripts]# sh 6_35_2.sh 2 6
2 < 6
[root@oldboy scripts]# sh 6_35_2.sh oldboy oldgirl
Pls input two num again.

范例:打印选择菜单,按照选择项一键安装不同的Web服务。

示例菜单:

bash 复制代码
[root@oldboy scripts]# sh menu.sh
    1.[install lamp]
    2.[install lnmp]
    3.[exit]
    pls input the num you want:

要求:

  • 1)当用户输入1时,输出"start installing lamp"提示,然后执行/server/scripts/lamp.sh输出"lamp isinstalled",并退出脚本,此为工作中所用的lamp一键安装脚本。
  • 2)当用户输入2时,输出"start installing lnmp"提示,然后执行/server/scripts/lnmp.sh输出"lnmp isinstalled",并退出脚本,此为工作中所用的lnmp一键 安装脚本。
  • 3)当输入3时,退出当前菜单及脚本。
  • 4)当输入任何其他字符时,给出提示"Input error"后退出脚本。
  • 5)对执行的脚本进行相关的条件判断,例如:脚本文件是否存在,是否可执行等的判断,尽量使用前面讲解过的知识点。

先来看一个解答前的热身示例脚本。打印简单的所选菜单示例1。

bash 复制代码
[root@oldboy scripts]# cat 6_36_1.sh
cat <<END  #<==这里的cat用法就是官方所说的here文档用法,其实就是按指定格式打印多行文本。
    1.panxiaoting
    2.gongli
    3.fanbinbing
END  #<==注意顶格写,开头不要带有空格,END成对出现,可以用任意的成对字符来替代,不要和内容冲突即可。
read -p "Which do you like? Pls input the num:" a  #<==读入用户的选择,赋值给变量a。
[ "$a" = "1" ] &&{  #<==条件表达式判断a的值是否为1,注意,这里推荐用字符串的语法格式判断。
    echo "I guess, you like panxiaoting"  #<==根据用户选择的结果,回应应用输出。
    exit 0 #<==退出脚本,不再向下执行。
}
[ "$a" = "2" ] &&{ #<==条件表达式判断a的值是否为2。
    echo "I guess, you like gongli"
    exit 0  #<==退出脚本,不再向下执行。
}
[ "$a" = "3" ] &&{ #<==条件表达式判断a的值是否为3。
    echo "I guess, you like fangbingbing"
    exit 0  #<==退出脚本,不再向下执行。
}
[[ ! "$a" =~ [1-3] ]] &&{  #<==这里就是用到了[[]]的通配符匹配的用法,即a是否为1或2或3。
    echo "I guess, you are not man."
}

执行结果如下:

bash 复制代码
[root@oldboy scripts]# sh 6_36_1.sh
    1.panxiaoting
    2.gongli
    3.fanbinbing
Which do you like? Pls input the num:1 #<==符合所选菜单的数字要求,给予选择结果的提示。
I guess, you like panxiaoting
[root@oldboy scripts]# sh 6_36_1.sh
    1.panxiaoting
    2.gongli
    3.fanbinbing
Which do you like? Pls input the num:2 #<==符合所选菜单的数字要求,给予选择结果的提示。
I guess, you like gongli
[root@oldboy scripts]# sh 6_36_1.sh
    1.panxiaoting
    2.gongli
    3.fanbinbing
Which do you like? Pls input the num:3 #<==符合所选菜单的数字要求,给予选择结果的提示。
I guess, you like fangbingbing
[root@oldboy scripts]# sh 6_36_1.sh
    1.panxiaoting
    2.gongli
    3.fanbinbing
Which do you like? Pls input the num:5 #<==不符合所选菜单的数字要求,给予非法输入的提示。
I guess, you are not man.
[root@oldboy scripts]# sh 6_36_1.sh
    1.panxiaoting
    2.gongli
    3.fanbinbing
Which do you like? Pls input the num:99 #<==不符合所选菜单的数字要求,给予非法输入的提示。
I guess, you are not man.

6、测试表达式test、​​、​​\[​​]​、(())的区别总结

测试表达式的语法比较复杂且容易混淆,对于初学者,一定要给自己设定个知识边界,下表列出了测试表达式​、​​\[​​]​、(())、test的区别。

相关推荐
音草1 小时前
Linux 基础指令入门:常用命令、Shell 原理与权限管理
linux
aosky1 小时前
Linux 写内容到文件的几种方式
java·linux·服务器
吴声子夜歌2 小时前
Shell编程——函数
linux·运维·shell
PC2005-cloud2 小时前
Qdrant 连接失败排查:localhost 解析到 __1,而 Docker 只绑了 IPv4
运维·docker·容器
电子制造自留地2 小时前
AI服务器PCB的超低损耗材料选型逻辑
运维·服务器·人工智能·科技·制造·科普·pcb工艺
码少女3 小时前
Linux——多路复用之poll模型
linux·服务器·网络
蓝速科技3 小时前
会议室门牌触控预约选型与落地实战指南丨蓝速科技
大数据·运维·数据库·人工智能·科技
PatrickYao04223 小时前
Linux安装UV
linux·运维·uv
蓝速科技3 小时前
企业展厅数字人导览效果提升与选型实战指南丨蓝速科技
大数据·运维·数据库·人工智能·科技·microsoft