一、读系统脚本,学条件测试的写法
1. 如何阅读优秀的脚本
变量测试必须加引号:
ubuntu@VM-0-9-ubuntu:~/shell$ file1=jitaimei.jpg
ubuntu@VM-0-9-ubuntu:~/shell$ [ -f "$file1" ] && echo "我是两年半练习生" || echo "我是好人"
我是两年半练习生
想要学好 shell 编程,能够解决日常工作的程度,想进阶到高阶 shell 开发,必然要看优秀脚本:
# 截取了一段linux系统脚本
root@VM-0-9-ubuntu:/etc/init.d# cat ssh
#! /bin/sh
### BEGIN INIT INFO
# Provides: ssh sshd
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: OpenBSD Secure Shell server
### END INIT INFO
set -e
# /etc/init.d/ssh: start and stop the OpenBSD "secure shell(tm)" daemon
test -x /usr/sbin/sshd || exit 0
( /usr/sbin/sshd -\? 2>&1 | grep -q OpenSSH ) 2>/dev/null || exit 0
umask 022
if test -f /etc/default/ssh; then
. /etc/default/ssh
fi
. /lib/lsb/init-functions
if [ -n "$2" ]; then
SSHD_OPTS="$SSHD_OPTS $2"
fi
centos 的 network 脚本也是这个套路:先 . /etc/init.d/functions 把函数库加载进来,再用 [ ! -f ... ]、[ "${NETWORKING}" = "no" ] && exit 6 做前置检查。
![CentOS 的 /etc/init.d/network:source 函数库之后,一串 [] 做条件判断,中间还有 CWD=$(pwd) 和 cd /etc/sysconfig/network-scripts](images/sh2-09.jpg)
2. 字符串值判断逻辑

= 判断是否相等,! 取反,!= 不等于。
mysql 自带的脚本用到了这些符号:
su_kill() {
if test "$USER" = "$user"; then
# 如果两个值相等,杀掉进程日志扔掉
kill $* >/dev/null 2>&1
else
su - $user -s /bin/sh -c "kill $*" >/dev/null 2>&1
fi
}
注意:
- 对于字符串的比较必须加双引号
- 使用等于号的判断,左右必须有空格(不加空格就是变量赋值了)
# 求字符串是否相等
ubuntu@VM-0-9-ubuntu:~/shell$ [ "yuchao" = "chaochao" ] && echo ok || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ name=yuchao
ubuntu@VM-0-9-ubuntu:~/shell$ echo $name
yuchao
ubuntu@VM-0-9-ubuntu:~/shell$ [ "$name" = "yuchao" ] && echo ok || echo no
ok
ubuntu@VM-0-9-ubuntu:~/shell$ [ "$name" = "yuchaooo" ] && echo ok || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ [ "$name" != "yuchaooo" ] && echo ok || echo no
ok
# ! -f 对结果取反
ubuntu@VM-0-9-ubuntu:~/shell$ [ -f "ccll.txt" ] && echo ok || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ [ ! -f "ccll.txt" ] && echo ok || echo no
ok
3. 数值比较符号
![数值比较符号对照:[] 和 test 里用 -eq -ne -gt -ge -lt -le,(( )) 和 [[]] 里可以直接用 == != > >= < <=](images/sh2-11.jpg)
用数学符号
# 这个结果是错的
ubuntu@VM-0-9-ubuntu:~/shell$ [ 0 > 1 ] && echo ok || echo no
ok
ubuntu@VM-0-9-ubuntu:~/shell$ [ 0 \> 1 ] && echo ok || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ [ 1 != 1 ] && echo ok || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ [ 1 != 3 ] && echo ok || echo no
ok
中括号使用数学比较符号要添加转义符号 \,!= 不用加。
用字符符号
# 大于 -gt
ubuntu@VM-0-9-ubuntu:~/shell$ [ 2 -gt 13 ] && echo ok || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ [ 2 -gt 1 ] && echo ok || echo no
ok
test 和中括号变量加引号的原因
- 变量可能为空:
[ -f $file ],$file为空时就变成[ -f ],直接报错 - 变量含空格:
[ $name = "john" ],值会被拆成多个参数,报错 - 变量含特殊字符:
[ $path = "/a/b" ],*会被展开成文件名列表
ubuntu@VM-0-9-ubuntu:~/shell$ n1=98;n2=99
ubuntu@VM-0-9-ubuntu:~/shell$ test "$n1" -eq "$n2" && echo yes || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ test "$n1" -lt "$n2" && echo yes || echo no
yes
# test支持字符和数学符号不用转义
ubuntu@VM-0-9-ubuntu:~/shell$ test "$n1" != "$n2" && echo yes || echo no
yes
ubuntu@VM-0-9-ubuntu:~/shell$ test "$n1" = "$n2" && echo yes || echo no
no
test 和 [] 语法都支持字符和数学符号,中括号需要转义。
双中括号
对单中括号的补充,可以使用正则表达式,不需要转义符号,自动识别数学符号。
实际使用最多的是单中括号:
# 符号和字符都支持
ubuntu@VM-0-9-ubuntu:~/shell$ [[ 5 = 6 ]] && echo ok || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ [[ 5 -eq 6 ]] && echo ok || echo no
no
系统自带的脚本也用了很多条件测试。
4. 逻辑运算符号
对应参数
&& 和 -a,|| 和 -o。
and 是递进,左边为真执行右边;or 是左边为假执行右边,左边为真直接跳过右边。
中括号逻辑运算比较
# 也可以写目录路径
ubuntu@VM-0-9-ubuntu:~/shell$ file1=calculation.sh
ubuntu@VM-0-9-ubuntu:~/shell$ file2=calculation2.sh
ubuntu@VM-0-9-ubuntu:~/shell$ [ -f "$file1" -a -f "$file2" ] && echo yes || echo no
yes
ubuntu@VM-0-9-ubuntu:~/shell$ [ -f "$file1" -o -f "$f2" ] && echo yes || echo no
yes
ubuntu@VM-0-9-ubuntu:~/shell$ [ -f "$file1" || -f "$f2" ] && echo yes || echo no
-bash: [: missing `]'
-f: command not found
中括号的逻辑判断无法使用 && 和 ||,只能使用字符参数。
双中括号逻辑运算比较
# -n 判断字符长度是否为0,非空为真
ubuntu@VM-0-9-ubuntu:~/shell$ a=""
ubuntu@VM-0-9-ubuntu:~/shell$ b="yuyu"
ubuntu@VM-0-9-ubuntu:~/shell$ test -n "$a" && echo yes || echo no
no
ubuntu@VM-0-9-ubuntu:~/shell$ [[ -n "$a" && "$a" = "$b" ]] && echo yes || echo no
no
# 使结果为真
# 改 a 后条件取反
ubuntu@VM-0-9-ubuntu:~/shell$ a=cc
ubuntu@VM-0-9-ubuntu:~/shell$ [[ -n "$a" && ! "$a" = "$b" ]] && echo yes || echo no
yes
# a 有值,后条件改为 !=(此处省略)
# a 改为b的值(此处省略)
二、逻辑运算脚本
1. 判断用户输入
脚本根据需求开发,需求:接收用户输入,判断是否等于某个数字。
ubuntu@VM-0-9-ubuntu:~/shell$ cat test_and_or.sh
#!/bin/bash
read -p "输入一个字符" var1
# 逻辑测试
[ "$var1" -eq "1" ] && {
echo $var1
exit 0
}
[ "$var1" = "2" ] && {
echo $var1
exit 0
}
# 限制只能输入1或2否则错误
[ "$var1" != "2" -a "$var1" != "1" ] && {
echo "发生错误,必须输入1或2"
exit 1
}
ubuntu@VM-0-9-ubuntu:~/shell$ bash test_and_or.sh
输入一个字符q
test_and_or.sh: line 6: [: q: integer expression expected
发生错误,必须输入1或2
ubuntu@VM-0-9-ubuntu:~/shell$ bash test_and_or.sh
输入一个字符2
2
可以使用 vim 写代码,也可以写完了粘贴到 vim 中。
2. lamp / lnmp 安装脚本
需求:安装 lnmp/lamp 脚本开发。模拟创建两个安装脚本 lnmp.sh、lamp.sh。
为了减少 bug,找到原始安装脚本,然后根据选择安装其中一个。
开发脚本一定会有真和假的两种情况,优先处理错误的逻辑情况,因为错误的逻辑情况最好处理。
[[ $num =~ [1-3] ]] 其中 =~ 表示使用正则,表示 num 变量必须为 1-3 之间的:
root@VM-0-9-ubuntu:~/shell/test_scripts# cat lamp_and_lnmp.sh
#!/bin/bash
# 判断脚本目录是否存在
# 不要覆盖系统的大写PATH变量
#
path=/root/shell/test_scripts
[ ! -d "$path" ] && mkdir $path -p
# 脚本正常逻辑
cat <<END
1.[install lamp]
2.[install lnmp]
3.[exit]
Please input the num you want:
END
read num
# 根据num变量进行逻辑处理
# 限制num输入为数字
expr $num + 1 &> /dev/null # 不是数字就会报错
# 判断上个结果,不为0就错
[ $? -ne 0 ] && {
echo "输入必须为 1|2|3|"
exit 1
}
# 对输入的数字判断1,2,3
[ "$num" -eq "1" ] && {
echo "开始安装lamp...waiting..."
# 这里的是伪代码 生产环境写安装代码
#
# 对文件权限判断是否可执行
[ -x "$path/lamp.sh" ] || {
echo "文件没有执行权限"
exit 1
}
source $path/lamp.sh
exit $?
}
[ "$num" -eq "2" ] && {
echo "开始安装lnmp...waiting..."
[ -x "$path/lnmp.sh" ] || {
echo "文件没有执行权限"
exit 2
}
source $path/lnmp.sh
exit $?
}
[ "$num" -eq "3" ] && {
echo "bye~"
exit 3
}
# 下面和if的用法一样,只是重复写了脚本,二选一就行
# 限制用户必须输入1,2,3
# 支持正则的[[]]
# 输入不为1-3时
[[ ! "$num" =~ [1-3] ]] && {
echo "输入的数字必须为1-3之间"
exit 4
}
执行效果:
root@VM-0-9-ubuntu:~/shell/test_scripts# bash lamp_and_lnmp.sh
1.[install lamp]
2.[install lnmp]
3.[exit]
Please input the num you want:
1
开始安装lamp...waiting...
lamp is installed
root@VM-0-9-ubuntu:~/shell/test_scripts# bash lamp_and_lnmp.sh
1.[install lamp]
2.[install lnmp]
3.[exit]
Please input the num you want:
2
开始安装lnmp...waiting...
lnmp is installed
root@VM-0-9-ubuntu:~/shell/test_scripts# bash lamp_and_lnmp.sh
1.[install lamp]
2.[install lnmp]
3.[exit]
Please input the num you want:
3
bye~
补充内容(如图)

最常用的是中括号搭配字符型参数。
至此 shell 进阶篇结束。

写脚本的直觉基本就来自这一页:先看系统自带的 /etc/init.d 脚本怎么用 test 和 [] 做前置检查, 再记住“能用 [[ ]] 就用 [[ ]]、变量一定加引号、&& 和 -a 别混着写”, 最后按“先处理错误分支、再处理正常分支”的顺序把逻辑铺出来,脚本就不会太乱。