#!/bin/sh _=$(command -v docker); if [ "$?" != "0" ]; then printf -- 'You don't seem to have Docker installed.n'; printf -- 'Get it: https://www.docker.com/community-editionn'; printf -- 'Exiting with code 127...n'; exit 127; fi; # ...
独立于当前工作目录
从不同的目录执行脚本可能会发生错误,这样的脚本没有人会喜欢。要解决这个问题,请使用绝对路径(/path/to/something)和脚本的相对路径(如下所示)。
可以使用dirname $0引用脚本的当前路径:
#!/bin/sh
CURR_DIR="$(dirname $0);"
printf -- 'moving application to /opt/app.jar';
mv "${CURR_DIR}/application.jar" /opt/app.jar;
如何读取输入:环境变量 vs. 标记
脚本通过两种方式接受输入:环境变量和选项标记(参数)。根据经验,对于不影响脚本行为的值,可以使用环境变量,而对于可能触发脚本不同流程的值,可以使用脚本参数。
不影响脚本行为的变量可能是访问令牌和 ID 之类的东西:
#!/bin/sh # do this export AWS_ACCESS_TOKEN='xxxxxxxxxxxx'; ./provision-everything # and not ./provisiong-everything --token 'xxxxxxxxxxx';
影响脚本行为的变量可能是需要运行实例的数量、是异步还是同步运行、是否在后台运行等参数:
#!/bin/sh # do this ./provision-everything --async --instance-count 400 # and not INSTANCE_COUNT=400 ASYNC=true ./provision-everything
打印对系统执行的所有操作
脚本通常会对系统执行有状态的更改。不过,由于我们不知道用户何时会向发送SIGINT,也不知道脚本错误何时可能导致脚本意外终止,因此很有必要将正在做的事情打印在终端上,这样用户就可以在不去查看脚本的情况下回溯这些步骤:
#!/bin/sh printf -- 'Downloading required document to ./downloaded... '; wget -o ./downloaded https://some.site.com/downloaded; printf -- 'Moving ./downloaded to /opt/downloaded...'; mv ./downloaded /opt/; printf -- 'Creating symlink to /opt/downloaded...'; ln -s /opt/downloaded /usr/bin/downloaded;
在必要时提供--silent选项
有些脚本是为了将其输出传给其他脚本。虽说脚本都应该能够单独运行,不过有时候也有必要让它们把输出结果传给另一个脚本。可以利用stty -echo来实现--silent标记:
#!/bin/sh
if [ ${#@} -ne 0 ] && [ "${@#"--silent"}" = "" ]; then
stty -echo;
fi;
# ...
# before point of intended output:
stty +echo && printf -- 'intended outputn';
# silence it again till end of script
stty -echo;
# ...
stty +echo;
exit 0;
重新开启显示
在使用stty -echo关闭脚本显示之后,如果发生致命错误,脚本将终止,而且不会恢复终端输出,这样对用户来说是没有意义的。可以使用trap来捕捉SIGINT和其他操作系统级别的信号,然后使用stty echo打开终端显示:










