Bash中set用法初学

一转眼到了2018年3月份,时光穿梭飞快!哎,不感性了,来点理性东西!今天要说下是Bash中的set命令。

set -u

执行脚本的时候,如果遇到不存在的变量,Bash 默认忽略它。而在脚本顶部加上set -u,遇到不存在的变量就会报错,并停止执行。

1
2
3
4
5
6
#!/bin/bash

set - u

echo $a
echo bar

上面 $a 是一个未定义的变量,则执行脚本会报错。

-u还有另一种写法-o nounset,两者是等价的。

1
set -o nounset

set -x

默认情况下,脚本执行后,屏幕只显示运行结果,没有其他内容。如果多个命令连续执行,它们的运行结果就会连续输出。有时会分不清,某一段内容是什么命令产生的。

set -x用来在运行结果之前,先输出执行的那一行命令。对调试脚本来说非常有用。

1
2
3
4
5
#!/bin/bash

set - x

echo bar

执行上面的脚本,结果如下:

1
2
+echo bar
bar

执行echo bar之前,该命令会先打印出来,行首以+表示。

set -e

之前我们说脚本里有未定义的变量,脚本会默认往下执行。同理如果脚本里面有运行失败的命令(返回值非0),Bash 也会继续执行后面的命令。

那么 set -e 使脚本发生错误就会终止执行

1
2
3
4
5
6
#!/bin/bash

set - e

foo
echo bar

执行结果如下:

1
script.sh: 行4: foo: 未找到命令

set -e根据返回值来判断,一个命令是否运行失败。但是,某些命令的非零返回值可能不表示失败,或者开发者希望在命令失败的情况下,脚本继续执行下去。这时可以暂时关闭set -e,该命令执行结束后,再重新打开set -e。

1
2
3
set + e
command2
set - e

还有一种方法是使用command || true,使得该命令即使执行失败,脚本也不会终止执行。

1
2
3
4
5
6
#!/bin/bash

set - e

foo || true
echo bar

set -o pipefail

set -e有一个例外情况,就是不适用于管道命令。

所谓管道命令,就是多个子命令通过管道运算符(|)组合成为一个大的命令。Bash 会把最后一个子命令的返回值,作为整个命令的返回值。也就是说,只要最后一个子命令不失败,管道命令总是会执行成功,因此它后面命令依然会执行,set -e就失效了。

1
2
3
4
5
#!/bin/bash
set - e

foo | echo a
echo bar

执行结果如下:

1
2
3
a
script.sh: 行4: foo: 未找到命令
bar

上面代码中,foo是一个不存在的命令,但是foo | echo a这个管道命令会执行成功,导致后面的echo bar会继续执行。

set -o pipefail用来解决这种情况,只要一个子命令失败,整个管道命令就失败,脚本就会终止执行。

1
2
3
4
5
#!/bin/bash
set -eo pipefail

foo | echo a
echo bar

运行后,结果如下:

1
2
a
script.sh: 行4: foo: 未找到命令

可以看到,echo bar没有执行。

总结

我们通常在bash脚本顶部加上 -eu。

1
2
3
#!/bin/bash

set - eu

除了直接在脚本的头部通过set设置外,我们也可以在执行脚本的时候通过传参的形式设置。

1
bash - euxo pipefail script.sh