set 在脚本中的意义
我们经常看到别人写的脚本中第一行会写一个set,这个set是什么意思呢?
set -o nounset
set -o errexit
set -euo pipefail
....
这都是什么意思呢?
这些set命令都是为了完善shell脚本的逻辑严谨性。
1、在高级语言中如果一个变量未定义会报错,在shell中则不会报错。如何避免这个问题呢?
set -o nounset 或者set -u
‘-u’: 遇到未定义的变量,报错并立即退出。
2、还有一种情况,当使用shell 退出码做判断的时候,如果遇到如下情况
退出码竟然是0. 显然awk语法有问题,如何让这个命令的退出码变为非0呢?
使用-o pipefail
再看这个完成的例子:
$ cat try_bash_o.sh #!/bin/bash set -e -o pipefail # 'foo' is a non-existing command foo | echo "a" echo "bar" $ ./try_bash_o.sh a ./try_bash_o.sh: line 5: foo: command not found $ echo $? 127 $ sed -i 's/-o pipefail//' try_bash_o.sh $ ./try_bash_o.sh a ./try_bash_o.sh: line 5: foo: command not found bar $ echo $? 0
有时候 这会产生意料之外的错误。
某天IT同事问我,根目录下为何有很多压缩文件,每天一个。
经过一番排查发现这里面的内容竟然是一样的,都是根目录下的*.log文件。每天0点定时生成压缩文件。第一时间想到了crontab。
果然发现了下面这个脚本。
由于cd的目录不存在,则直接变成了根目录,所以才会出现上面的情况。
3、当一个命令失败时,立即退出,如果你不想立即退出则可用‘|| true’
使用-e参数 或者set -o errexit。
一般我们编写的脚本都是需要用到-e的,如果某一步失败不影响结果应该在脚本逻辑中处理而不是使用|| true
bash 中的算数计算
1、算术扩展 (())
echo $((3+2)) # 使用变量也可以 a=3 echo $((${a}+2)) 5
2、expr
expr 是 coreutils 软件包提供的一个命令,可对表达式进行计算,或者比较大小之类的。
x=3 y=7 # 注意加号左右一定要有空格 expr $x + $y 10
3、bc
bc 其实是一种支持任意精度和可交互执行的计算语言。它比上述提到的 expr
要强大的多,尤其是它还支持浮点数运算。
bc zsh好像默认不支持,bash4.2 是默认支持的
sqrt()
快速的计算平方根。
echo "scale=2;7/3"|bc 2.33 echo "7/3"|bc 2 echo "scale=2;sqrt(6)" |bc 2.44 echo "scale=2;sqrt(9)" |bc 3.00
bash调试模式
-v 开启详细模式,用于查看所执行的命令, 例如bash -x,或者在脚本中set -x来开启此模式
-x 进入 xtrace 模式,用于调试执行阶段的变量值。
-u 检查变量是否未定义/绑定
cat add.sh #!/bin/bash five=5 ten=10 total=$((five+tne)) echo $total bash add.sh 5 bash -u add.sh add.sh: line 4: tne: unbound variable
let i=0的坑
有如下一段代码,执行之后是什么结果呢?
#!/bin/bash set -e -u let i=0 while [ $i -lt 6 ]; do echo $i ((i++)) done
啥都没输出
调试模式
# bash -x tmp.sh + set -e -u + let i=0
其实这里有一个坑,就是let的返回值,0被认为是不可计算的值,所以状态码返回1
触发了set -e 就直接return了。
看看官方这说法
let arg [arg ...] Each arg is an arithmetic expression to be evaluated (see ARITHMETIC EVALUATION above). If the last arg evaluates to 0, let returns 1; 0 is returned otherwise.