你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

如何写出健壮,可靠的shell脚本?

[复制链接]
gaosmile 发布时间:2020-8-16 21:51
如果秉承着能跑就行的态度写shell脚本,是很自在的,但是如果你想要写出健壮,可靠的shell脚本,可没那么容易。那么有什么可操作的经验或者方法吗?
语法检查
第一个最简单的方法就是利用工具对脚本进行检查,它能最大程度地发现shell脚本中存在的语法错误,如果你还不知道,建议你绝对不要错过。
而为了保证脚本健壮可靠,那么就需要保证脚本在一些特殊的情况及早出现,避免漏网之鱼。来看看有哪些可行的技巧。
脚本失败时即退出
可以在脚本的开头设置如下:
set -e' O! _5 n. w) `
举个例子:
#!/bin/bash( E! ^; m- \& t6 G2 I* x
set -e0 @% Q7 P1 w) `5 p
lp    #这里运行会出错7 l& \: U$ s4 p) ?; [( F  [
date
; U2 |4 X6 z! h0 g, I
这种情况下,运行一旦出错就会退出,不放过一个问题:
$ ./test.sh
( Y8 L  k4 ^8 j2 {$ Blp: Error - no default destination available.
% h- b. ]% f; x) e( b
当然了,这也有不好的地方,有时候命令执行本来就可能是失败的,你还是希望它继续,可以临时加上|| true:
#!/bin/bash
! }9 {" {  O1 G% q1 N2 mset -e
3 W3 H! u! u% m2 [) B% o! G( ]lp  || true  
9 C/ f; x$ ]) o1 @1 N( t) c+ Rdate

; ?+ S, I  S2 ^- D6 L$ ^4 m
不过个人觉得这样的设置用处不是特别大,因为很多时候就是需要处理不同的错误情况,而这样只能要么遇到错误退出,要么认为正确,导致无法走到异常分支。( G3 M2 G' |% i7 |
通过set +e设置回来:
set -e* ~( E, x3 x" e! L, }9 M
#command
" u$ Q" \) P# |# G- U4 J+ cset +e. J* o: u: ?/ x9 H1 r. E/ f
#other command
( l' n+ l+ j% ?; n: c
打印脚本执行过程
调试阶段,你可能想知道整个过程是执行了哪些命令,每条命令都具体执行了什么,可以利用下面的方式执行:
sh -x test.sh2 M" C/ M& k; C: \
或者,和上面类似,在开头加上set -x:
//来源:公众号【编程珠玑】
6 a$ l( ~" c7 z5 G3 X: V4 U//作者:守望先生
' n3 O# m; m. S1 I: o( R# Z* h$ X#!/bin/bash
6 R4 i$ n: o; Vset -x* u0 W6 P3 A- Q
if [ $# -lt 1 ]
# o$ J8 j, ]% ?0 u8 m; t9 p6 Sthen2 {! K6 z" f0 {6 q1 w9 L2 H7 y+ ?
   echo  "no para": P. @$ c* r& _( A  y! i
else
0 @/ ^* ?, @2 J( F+ e6 \   echo "para 1 $1"; |" z1 K2 t0 h1 q" b& X
fi

9 a3 O% W( N& w  W2 N- T
执行时,输出如下:
+ [ 0 -le 1 ]
. ?) v# v  V+ l, b* Y5 T/ b8 P+ echo no para5 R' t2 d! s; t, d5 l: Q" Q
no para
5 s  e$ e9 r. Y
前面带+的内容就是命令实际执行的,你可以看到比较条件是什么,变量被展开成了具体内容,走到了哪个分支,非常清楚。
显示未定义的变量
shell中变量没有定义,仍然是可以使用的,但是它的结果可能不是你所预期的。举个例子:
//来源:公众号【编程珠玑】
  o* b9 `! O  a% L2 y# z//作者:守望先生
#!/bin/bash" W+ t1 w0 [) b9 E7 J+ e
if [ "$var" = "abc" ]
' v0 T# n+ v1 I7 T4 O$ B9 h6 ]then, e! M, Z" u% ^
   echo  " not abc"
4 B( H" S+ @' `2 Celse
5 f+ o; X/ e0 ^   echo " abc "
& r( y9 _* f, S7 m3 E$ @fi
' d3 G7 e! i& K0 X
这里本来想判断var的内容是否为abc,实际上var并没有定义,但是在这里使用并没有报错,如果我们想早点发现这类问题,避免在复杂的脚本中问题被掩盖,那么可以在开头加上:
set -u/ [" Y9 c0 I( W# g7 Z% ?$ a
再次运行就会提示:
test.sh: 5: test.sh: num: parameter not set
8 Z! _4 F' d3 d
再想象一下,你本来想删除:
rm -rf $dir/*6 z6 J0 g; R; V
然后dir是空的时候,变成了什么?
是不是有种后背发凉的感觉?
管道命令一个失败时整个失败
有时候我们可能会执行类似这样的命令:
cat test.sh |grep if | cut -d ';' -f 2" B2 f0 P* ?0 f2 o3 u  p  A1 r
三条命令一行执行,如果我们希望在其中一条失败,整个命令就失败,而避免执行后面无意义的命令,那么可以在开始设置:
set -o pipefail0 {6 I1 W/ X- W2 d& a$ [( q
不设置的情况下,cat test.sh即使执行失败了,后面的grep实际上还会继续执行,可能会导致一些意想不到的情况发生,如果不想这样的情况发生,那么这样设置是有帮助的。
对于静态变量使用readonly
通常我们会在脚本开头定义一些静态变量:
MY_PATH=/usr/bin
( {; h9 A8 T" N- s8 p/ m$ q8 q1 G
而为了避免MY_PATH被意外修改,可以这样:
readonly MY_PATH=/usr/bin4 U. m0 l( U  f
这样的话,一旦后面有命令尝试修改,就会报错。
#!/bin/bash) E( b- [1 j0 u" U# e
readonly MY_PATH=/usr/bin6 \9 P1 T' Z3 k4 d* k
MY_PATH=/usr/local/bin

7 i  v' ?7 w" u$ i0 n! h2 C
运行一下试试:
$ ./test.sh
. `9 ?. I' z/ J' S/ E( Y! Btest.sh: 3: test.sh: MY_PATH: is read only
' k8 J  p" I3 V) P# `3 |) F; D
看,给你提示了!
给变量设置可选的初始值
例如:
name=${1:-shouwang}: t) e' \4 T3 D2 {
echo "${name}"

; E, a& l/ S7 t% [
这里让name为$1,即第一个参数,而当它为空时,令name为shouwang。
多条命令执行使用&&
例如:
cmd0;cmd1;cmd1
' u$ m' e2 B; ~7 N0 P5 I: ?) ^, `; R
这里如果cmd0失败了,后面的命令仍然会执行,而如果不希望后面的命令执行,可以使用:
cmd0 && cmd1 && cmd12 r5 ~4 i' I7 W
使用函数
脚本本身比较短还好,而脚本一旦变长,不使用函数,将使得脚本很难维护,可读性也很差。
总结
实际上最开始介绍的脚本检查工具就已经非常有效了,基本的错误都能检查出来,而其他的内容,更多的是关注于脚本调试,不放过任何一个可能的错误。
最后,还是优先推荐shellcheck工具。

, }3 V* v" j7 c* p
赞 收藏 评论0 发布时间:2020-8-16 21:51

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版