在写shell的时候我们经常面临的问题一般都是:
这里会介绍shell脚本中的一个非常有用的命令,上面的错误可以通过这个命令来避免,脚本的调试也会变得非常容易,这个牛叉的命令就是set命令。
set 命令在没有参数的时候会显示当前环境的所有环境变量,比如直接执行 set
看一看到一大堆东西
set
带参数的命令可以用来设置shell的变量,他的功能还挺多的,今天我们主要学习的是和shell脚本的运行模式有关的命令,主要的命令有以下几个。
set -e
set +e
set -u
set +u
set -x
set +x
set -o pipefail
set +o pipefail
可以看到上面的命令基本都是成对出现的,一个是减号,一个是加号,我们可以理解为set命令是对shell的工作模式进行了设置
减号-
是开启了某种模式,加号 +
则是关闭对应的模式。后面我们会详细介绍。
set -e
的功能是遇到错误后脚本会直接退出,不会继续往下执行
我们先准备一个脚本 test.sh
cat test.sh
#!/bin/bash
aa=123
cat $aa
necho $aa
echo $aa
执行bash test.sh
cat: 123: No such file or directory
test.sh: line 5: necho: command not found
123
因为不存在名子为123的文件,所以cat命令执行报错,同样的,因为不存在necho命令,所以也会报错,关键是报错后都会继续向下执行,所以
echo $aa
还是会成功输出123, 但是很多时候我们希望在执行错误后能够立即退出,而不是继续向下执行,那么可以在代码前加一个set -e
$ cat test.sh
#!/bin/bash
set -e
aa=123
cat $aa
necho $aa
echo $aa
$ bash test.sh
cat: 123: No such file or directory
这样就ok了,遇到错误后脚本会直接退出,不会继续往下执行。
如果你只是想对其中的一段代码做这种的设置,那么你可以这样做
$ cat -n test.sh
1 #!/bin/bash
2
3 aa=123
4 cat $aa
5 set -e
6 echo $aa
7 set +e
8 necho $aa
9 echo $aa
执行
$ bash test.sh
cat: 123: No such file or directory
123
test.sh: line 8: necho: command not found
123
对于上面的文件set的作用域只是在第5-7行,因为第6行可以正确执行,所以整个脚本也就可以正确执行了。
这种局部开启的方式很多时候没有必要,尽量在文件头部加一个就好了,当然在有些时候还是很有必要的,比如你要判断某个子shell的执行结果,使用了$?
变量,那么就要关闭set了。
set -o pipefail
这个命令主要是对上面的set -e
的补充,因为set -e
对于管道符是无效的,比如上面的脚本假如变成
$ cat -n test.sh
1 #!/bin/bash
2
3 set -e
4 aa=123
5 cat $aa | echo
6 echo $aa
7
8
执行
$ bash test.sh
cat: 123: No such file or directory
123
可以看到使用管道符号|
的话set -e
也不好用了,这个时候假如使用 set -o pipefail
则可以解决这个问题
$ cat -n test.sh
1 #!/bin/bash
2
3 set -e
4 set -o pipefail
5
6 aa=123
7 cat $aa | echo
8 echo $aa
9
10
执行
$ bash test.sh
cat: 123: No such file or directory
可以看到cat失败后后面的不会再执行了。
执行脚本的时候,如果遇到不存在的变量,Bash 默认忽略它,set -u
可以在需要的变量不存在的时候直接报错退出
cat -n test.sh
1 #!/bin/bash
2
3 aa=123
4 echo $bb
5 echo $aa
执行
$ bash test.sh
123
可以看到,即使变量 bb不存在也不会报错,如果想要避免这种情况,可以这样做
$ cat -n test.sh
1 #!/bin/bash
2
3 set -u
4 aa=123
5 echo $bb
6 echo $aa
执行
$ bash test.sh
test.sh: line 5: bb: unbound variable
同样的,你也可以通过
set +u
关闭这个模式设置
有些时候我们写的脚本比价长也比较复杂,引起最终结果出错的原因可能是前面多个步骤运算出错导致的(程序没有语法错误,可能是赋值计算等出错)
这个时候如果通过echo的方式去调试每一步的值会非常麻烦,而且后面还要注释掉大量的echo语句。这个时候就可以通过set -x
来打开调试,让调试变得十分简单。
$ cat -n test.sh
1 #!/bin/bash
2
3
4 ip=`ifconfig eth0| awk 'NR==2{print $0}'|awk -F "[:]" '{print $2}'|awk '{print $1}'`
5 set -x
6 k=$ip"aaa"
7 set +x
8 echo "$k"
9
10
执行
$ bash test.sh
+ k=10.76.0.27aaa
+ set +x
10.76.0.27aaa
要是能够显示哪一行执行的结果就更好了,别怕,可以这样设置
$ cat -n test.sh
1 #!/bin/bash
2
3 export PS4='+{$LINENO:${FUNCNAME[0]}} '
4
5 ip=`ifconfig eth0| awk 'NR==2{print $0}'|awk -F "[:]" '{print $2}'|awk '{print $1}'`
6 set -x
7 k=$ip"aaa"
8 set +x
9 echo "$k"
10
11
再执行
$ bash test.sh
+{7:} k=10.76.0.27aaa
+{8:} set +x
10.76.0.27aaa
可以看到行号也显示出来了,其实冒号后面是要显示方法名的,因为这里没有使用方法,所以没有显示。下面给一个方法使用的样例。
$ cat -n test.sh
1 #!/bin/bash
2
3 export PS4='+{$LINENO:${FUNCNAME[0]}} '
4
5
6 function tool()
7 {
8
9 ip=`ifconfig eth0| awk 'NR==2{print $0}'|awk -F "[:]" '{print $2}'|awk '{print $1}'`
10 set -x
11 k=$ip"aaa"
12 set +x
13 echo "$k"
14
15 }
16
17 tool
18
执行
$ bash test.sh
+{11:tool} k=10.76.0.27aaa
+{12:tool} set +x
10.76.0.27aaa
通过上面的样例学习,可以了解到使用set命令可以使我们的shell变得更加安全,可预期。同时别忘了,set +
是可以关闭对应的模式的,这个在有些时候也是有必要的。
对于初开始写shell脚本的同学可以养成使用这个命令的习惯,会大大提升shell编写的安全感。
建议
set -e
set -u
set -o pipefail
都直接开启
对于复杂的脚本可以局部开启
set -x