shell脚本编辑工具_编写高效Shell脚本的13个秘诀,这都可以
作者|Joseph Matthias Goh译者|无明编辑|张婵 高效开发运维公众号最终用户通常不具备技术背景,所以他们不会关心产品的代码细节然而,脚本代码则是由开发人员编写的,用于其他开发人员你有没有遇到过这样的情况:你运行了./script.sh,脚本输出了一些信息,但你却不清楚它具体做了些什么。
这是一个糟糕的脚本用户体验本文将探讨如何编写具有出色开发者体验的shell脚本最终用户通常不具备技术背景,所以他们不会关心产品的代码细节然而,脚本代码则是由开发人员编写的,用于其他开发人员这往往会带来一些问题:。
混乱的脚本 —— 我们都是工程师,能读懂代码,但也要考虑到那些不太熟悉Shell脚本的人(就像我们在编写代码时也会考虑他们一样)充满日志和错误信息的输出 —— 即使我们也是工程师,也并不代表我们理解每一步。
搞砸了却不进行清理 —— 当然,我们可以手动撤销变更,但你会让那些信任你的脚本的人这样做吗?因此,我们可以采取一些措施来编写更好的Shell脚本所有示例都可以使用与POSIX标准兼容的shell运行(#!/bin/sh),因为这是最常用的。
如果您时间有限,可以只看下面的总结部分:提供--help标记检查所有命令的可用性独立于当前工作目录如何读取输入:环境变量 vs. 参数打印脚本对系统的操作如果需要,提供--silent选项重新启用显示以动画方式显示进度
用颜色编码输出出错立即退出脚本自行执行清理工作使用不同的错误码退出在结束时打印一个新行有时间的话可以接着往下看具体内容:提供 --help 标记安装在系统上的二进制文件通常带有man帮助文档,但脚本不一定有。
因此,我们需要为脚本提供-h或--help标记,以便打印关于如何使用脚本的信息这对于需要修改脚本的其他工程师也是一个有用的内联文档:#!/bin/sh if [ ${#@} -ne 0 ] && [ "${@#"--help"}" = "" ]; then printf -- ...帮助信息...\n; exit 0; fi;。
这段脚本首先计算参数长度(${#@} -ne 0),只有当参数长度不为零时才检查--help标记下一个条件会检查参数中是否存在字符串“--help”第一个条件是必需的,如果参数长度为零,则不需要打印帮助信息。
检查所有命令的可用性脚本通常会调用其他脚本或二进制文件在调用可能不存在的命令时,请先检查它们是否可用可以使用“command -v 命令名称”来执行此操作,看看它的退出代码是否为零如果命令不可用,可以告诉用户如何获取该二进制文件:。
#!/bin/sh _=$(command -v docker); if [ "$?" != "0" ]; then printf -- 您似乎没有安装Docker\n; printf -- 获取它: https://www.docker.com/community-edition\n; printf -- 退出并返回错误码127...\n; exit 127; fi; # ...。
独立于当前工作目录从不同的目录执行脚本可能会发生错误要解决这个问题,请使用绝对路径(/path/to/something)和脚本的相对路径(如下所示)可以使用dirname $0引用脚本的当前路径:#!/bin/sh CURR_DIR="$(dirname $0)"; printf -- 移动应用程序到 /opt/app.jar;\n; mv "${CURR_DIR}/application.jar" /opt/app.jar;
如何读取输入:环境变量 vs. 参数脚本通过两种方式接受输入:环境变量和参数根据经验,对于不影响脚本行为的值,可以使用环境变量;而对于可能触发脚本不同流程的值,则使用参数不影响脚本行为的变量可能是访问令牌和ID之类的东西:。
#!/bin/sh # 使用这种方式 export AWS_ACCESS_TOKEN=xxxxxxxxxxxx; ./provision-everything # 不要用这种方式 # ./provisiong-everything --token xxxxxxxxxxx;
影响脚本行为的变量可能是需要运行的实例数量、是否异步运行、是否在后台运行等参数:#!/bin/sh # 使用这种方式 ./provision-everything --async --instance-count 400 # 不要用这种方式 # INSTANCE_COUNT=400 ASYNC=true ./provision-everything
打印脚本对系统的操作脚本通常会对系统执行有状态的更改为了确保用户可以在不查看脚本的情况下了解这些更改,应在终端上打印这些操作:#!/bin/sh printf -- 正在下载所需文件到 ./downloaded... ; wget -o ./downloaded https://some.site.com/downloaded; printf -- 正在移动 ./downloaded 到 /opt/downloaded...; mv ./downloaded /opt/; printf -- 正在创建符号链接到 /opt/downloaded...; ln -s /opt/downloaded /usr/bin/downloaded;。
如果需要,提供 --silent 选项有些脚本是为了将其输出传递给其他脚本虽然脚本应该能够单独运行,但有时也有必要让它们将输出传递给另一个脚本可以使用stty -echo来实现--silent标记:#!/bin/sh if [ ${#@} -ne 0 ] && [ "${@#"--silent"}" = "" ]; then stty -echo; fi; # ... # 在输出之前: stty +echo && printf -- 预期输出\n; # 再次静默直到脚本结束 stty -echo; # ... stty +echo; exit 0;
重新启用显示在使用stty -echo关闭脚本显示后,如果发生致命错误,脚本将终止且不会恢复终端显示可以使用trap来捕捉SIGINT和其他操作系统级别的信号,然后使用stty echo重新启用显示:#!/bin/sh error_handle() { stty echo; } if [ ${#@} -ne 0 ] && [ "${@#"--silent"}" = "" ]; then stty -echo; trap error_handle INT; trap error_handle TERM; trap error_handle KILL; trap error_handle EXIT; fi; # ...
游戏狂热者必备良品!【星界云手机】以云端托管手游为核心,为你打造无与伦比的游戏体验。挂机脚本助你24小时不间断游戏,轻松练级打日常,让你成为游戏中的王者!
以动画方式显示进度有些命令需要很长时间才能完成,而并非所有脚本都提供了进度条可以通过一些方式告诉用户脚本仍在运行例如,在while循环中打印一些信息:#!/bin/sh printf -- 执行异步操作...; ./trigger-action; DONE=0; while [ $DONE -eq 0 ]; do ./async-checker; if [ "$?" = "0" ]; then DONE=1; fi; printf -- .; sleep 1; done; printf -- 完成!\n;。
或者可以做一些有趣的小玩意儿,例如:http://mywiki.wooledge.org/BashFAQ/034用颜色编码输出在脚本中调用其他二进制文件或脚本时,对它们的输出进行颜色编码,这样就可以知道哪个输出来自哪个脚本或二进制文件。
这样我们就不需要在满屏的黑白输出文本中查找想要的输出结果理想情况下,脚本应该输出白色(默认的,前台进程),子进程应该使用灰色(通常不需要,除非出现错误),使用绿色表示成功,红色表示失败,黄色表示警告#!/bin/sh printf -- 正在执行... \n; printf -- \033[37m 其他人的输出 \033[0m\n; printf -- \033[32m 成功: 太棒了 \033[0m\n; printf -- \033[33m 警告: 嗯嗯 \033[0m\n; printf -- \033[31m 错误: 啊哦 \033[0m\n;
可以使用\033[Xm,其中X代表颜色代码。有些脚本使用\e而不是\033,但要注意\e不适用于所有的UNIX系统。
正确示范可在.sh中使用的所有颜色和修饰符:https://misc.flogisoft.com/bash/tip_colors_and_formatting出错立即退出脚本set -e表示从当前位置开始,如果出现任何错误都将触发。
EXIT相反,set +e表示不管出现任何错误继续执行脚本如果脚本是有状态的(每个后续步骤都依赖前一个步骤),那么请使用set -e,在脚本出现错误时立即退出脚本如果要求所有命令都要执行完(很少会这样),那么就使用。
set +e#!/bin/sh set +e; ./script-1; ./script-2; # 不依赖于 ./script-1 ./script-3; # 不依赖于 ./script-2 set -e; ./script-4; ./script-5; # 依赖于 ./script-4的成功 # ...。
自行执行清理工作大多数脚本在出现错误时不会执行清理工作,能够做好这方面工作的脚本实属罕见,但这样做其实很有用,还可以省下不少时间前面已经给出过示例,让stty恢复正常,并借助trap命令来执行清理工作:。
#!/bin/sh handle_exit_code() { ERROR_CODE="$?"; printf -- "发生了错误,现在开始清理... "; # ... 清理代码 ... printf -- "完成。
\n退出并返回错误码 ${ERROR_CODE}\n"; exit ${ERROR_CODE}; } trap "handle_exit_code" EXIT; # ... 实际脚本...使用不同的错误码退出
在大多数shell脚本中,exit 0表示执行成功,exit 1表示发生错误对错误与错误码进行一对一的映射,这样有助于脚本调试#!/bin/sh # ... if [ "$?" != "0" ]; then printf -- X发生了。
退出并返回状态码1\n; exit 1; fi; # ... if [ "$?" != "0" ]; then printf -- Y发生了退出并返回状态码2\n; exit 2; fi;这样做还有一个额外的好处,就是其他脚本在调用你的脚本时,可以根据错误码来判断发生了什么错误。
在结束时打印一个新行如果你有在遵循脚本的最佳实践,那么可能会使用printf代替echo(它在不同系统中的行为有所差别)问题是printf在命令结束后不会自动添加一个新行,导致控制台看起来是这样的:
看起来是多么的平淡这并不好看,可以通过简单的方式打印一个新行:#!/bin/sh # ... 你的脚本 ... printf -- \n; exit 0;这样就可以得到:
好多了哈别人会感谢你这么做的。总 结这篇文章大致总结了一些简单易用的技巧,让shell脚本更易于调试和使用。
【星界云手机】解放你的双手,畅玩手游新境界!云端托管手游,24小时不间断游戏体验,让你轻松练级打日常。告别手续繁琐,只需一键连接,即刻进入游戏世界,开启全新的游戏方式!
本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:631580315@qq.com