我们知道,Bash 执行脚本的时候,会创建一个新的 Shell。
$ bash script.sh
上面代码中,script.sh是在一个新的 Shell 里面执行。这个 Shell 就是脚本的执行环境,Bash 默认给定了这个环境的各种参数。
set命令用来修改 Shell 环境的运行参数,也就是可以定制环境。一共有十几个参数可以定制,官方手册有完整清单,本文介绍其中最常用的四个。
1)set命令行下不带任何参数,直接运行set:
会显示所有的环境变量和 Shell 函数
$ set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.2.46(1)-release'
COLUMNS=210
DIRSTACK=()
DT=20191025_164714
...
2)set -u:
执行脚本的时候,如果遇到不存在的变量,Bash 默认忽略它。例如:
#脚本script.sh:
#!/usr/bin/env bash
echo $a
echo bar
#执行:
$ sh script.sh
bar
可以看到$a虽然不存在,但没有报错。大多数情况下,这不是开发者想要的行为,遇到变量不存在,脚本应该报错,而不是一声不响地往下执行。set -u就用来改变这种行为。脚本在头部加上它,遇到不存在的变量就会报错,并停止执行。
#!/usr/bin/env bash
set -u
echo $a
echo bar
$ sh script.sh
bash: script.sh:行4: a: 未绑定的变量
3)set -x:
默认情况下,脚本执行后,屏幕只显示运行结果,没有其他内容。如果多个命令连续执行,它们的运行结果就会连续输出。有时会分不清,某一段内容是什么命令产生的。set -x用来在运行结果之前,先输出执行的那一行命令。
#脚本 script.sh
#!/usr/bin/env bash
set -x
echo bar
#执行
$ sh script.sh
+ echo bar
bar
可以看到,执行echo bar
之前,该命令会先打印出来,行首以+
表示。这对于调试复杂的脚本是很有用的。
4)set -e:
如果脚本里面有运行失败的命令(返回值非0),Bash 默认会继续执行后面的命令。例如:
#脚本 script.sh
#!/usr/bin/env bash
foo
echo bar
#执行
$ sh script.sh
script.sh:行3: foo: 未找到命令
bar
可以看到,Bash 只是显示有错误,并没有终止执行,这种行为很不利于脚本安全和除错。set -e从根本上解决了这个问题,它使得脚本只要发生错误,就终止执行。例如:
#脚本 script.sh
#!/usr/bin/env bash
set -e
foo
echo bar
#执行
$ sh script.sh
script.sh:行4: foo: 未找到命令
可以看到,第4行执行失败以后,脚本就终止执行了。
set -e根据返回值来判断,一个命令是否运行失败。但是,某些命令的非零返回值可能不表示失败,或者开发者希望在命令失败的情况下,脚本继续执行下去。这时可以暂时关闭set -e,该命令执行结束后,再重新打开set -e。
set +e
command1
command2
set -e
5)set -o pipefail:
set -e有一个例外情况,就是不适用于管道命令。所谓管道命令,就是多个子命令通过管道运算符(|)组合成为一个大的命令。Bash 会把最后一个子命令的返回值,作为整个命令的返回值。也就是说,只要最后一个子命令不失败,管道命令总是会执行成功,因此它后面命令依然会执行,set -e就失效了。例如:
#脚本 script.sh
#!/usr/bin/env bash
set -e
foo | echo a
echo bar
#执行
$ sh script.sh
$ bash script.sh
a
script.sh:行4: foo: 未找到命令
bar
上面代码中,foo
是一个不存在的命令,但是foo | echo a
这个管道命令会执行成功,导致后面的echo bar
会继续执行。
set -o pipefail
用来解决这种情况,只要一个子命令失败,整个管道命令就失败,脚本就会终止执行。
#脚本 script.sh
#!/usr/bin/env bash
set -eo pipefail
foo | echo a
echo bar
#执行
$ sh script.sh
$ bash script.sh
a
script.sh:行4: foo: 未找到命令
原文: