编写可靠bash脚本的一些技巧

原作者:腾讯技术工程

原文链接:https://zhuanlan.zhihu.com/p/123989641

写过很多 bash 脚本的人都知道,bash 的坑不是一般的多。 其实 bash 本身并不是一个很严谨的语言,但是很多时候也不得不用。以下总结了一些鹅厂程序员在编写可靠 bash 脚本的一些小 tips。

0. set -x -e -u -o pipefail

在写脚本时,在一开始(Shebang 之后)就加上这一句,或者它的缩略版:

set -xeuo pipefail

这能避免很多问题,更重要的是能让很多隐藏的问题暴露出来。

下面说明每个参数的作用,以及一些例外的处理方式 :

-x : 在执行每一个命令之前把经过变量展开之后的命令打印出来。

这个对于 debug 脚本、输出 Log 时非常有用。 正式运行的脚本也可以不加。

-e : 遇到一个命令失败(返回码非零)时,立即退出。

bash 跟其它的脚本语言最大的不同点之一,应该就是遇到异常时继续运行下一条命令。 这在很多时候会遇到意想不到的问题。加上 -e ,会让 bash 在遇到一个命令失败时,立即退出。

如果有时确实需要忽略个别命令的返回码,可以用 || true 。如:

some_cmd || true        # 即使some_cmd失败了,仍然会继续运行
some_cmd || RET=$?      # 或者可以这样来收集some_cmd的返回码,供后面的逻辑判断使用

但是在管道串起多条命令的情况下,只有最后一条命令失败时才会退出。如果想让管道中任意一条命令失败就退出,就要用后面提到的-o pipefail 了。

加-e 有时候可能会不太方便,动不动就退出。但觉得还是应该坚持所谓的fail-fast 原则,也就是有异常时停止正常运行,而不是继续尝试运行可能存在缺陷的过程。如果有命令可以明确忽略异常,那可以用上面提到的 || true 等方式明确地忽略之。

-u :试图使用未定义的变量,就立即退出。

如果在 bash 里使用一个未定义的变量,默认是会展开成一个空串。有时这种行为会导致问题,比如:

rm -rf $MYDIR/data

如果 MYDIR 变量因为某种原因没有赋值,这条命令就会变成 rm -rf /data 。 这就比较搞笑了。。 使用 -u 可以避免这种情况。

但有时候在已经设置了-u 后,某些地方还是希望能把未定义变量展开为空串,可以这样写:

${SOME_VAR:-}#  bash变量展开语法,可以参考:
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

-o pipefail : 只要管道中的一个子命令失败,整个管道命令就失败。

pipefail 与-e 结合使用的话,就可以做到管道中的一个子命令失败,就退出脚本。

1. 防止重叠运行

在一些场景中,我们通常不希望一个脚本有多个实例在同时运行。比如用 crontab 周期性运行脚本时,有时不希望上一个轮次还没运行完,下一个轮次就开始运行了。 这时可以用 flock 命令来解决。 flock 通过文件锁的方式来保证独占运行,并且还有一个好处是进程退出时,文件锁也会自动释放,不需要额外处理。

用法 1: 假设你的入口脚本是 myscript.sh,可以新建一个脚本,通过 flock 来运行它:

# flock --wait 超时时间   -e 锁文件   -c "要执行的命令"
# 例如:
flock  --wait 5  -e "lock_myscript"  -c "bash myscript.sh"

用法 2: 也可以在原有脚本里使用 flock。 可以把文件打开为一个文件描述符,然后使用 flock 对它上锁(flock 可以接受文件描述符参数)。

exec 123<>lock_myscript   # 把lock_myscript打开为文件描述符123
flock  --wait 5  123 || { echo 'cannot get lock, exit'; exit 1; }

2. 意外退出时杀掉所有子进程

我们的脚本通常会启动好多子脚本和子进程,当父脚本意外退出时,子进程其实并不会退出,而是继续运行着。 如果脚本是周期性运行的,有可能发生一些意想不到的问题。

在 stackoverflow 上找到的一个方法,原理就是利用 trap 命令在脚本退出时 kill 掉它整个进程组。 把下面的代码加在脚本开头区,实测管用:

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT

不过如果父进程是用 SIGKILL (kill -9) 杀掉的,就不行了。因为 SIGKILL 时,进程是没有机会运行任何代码的。

3. timeout 限制运行时间

有时候需要对命令设置一个超时时间。这时可以使用 timeout 命令,用法很简单:

timeout 600s  some_command arg1 arg2

命令在超时时间内运行结束时,返回码为 0,否则会返回一个非零返回码。

timeout 在超时时默认会发送 TERM 信号,也可以用 -s 参数让它发送其它信号。

4. 连续管道时,考虑使用 tee 将中间结果落盘,以便查问题

有时候我们会用到把好多条命令用管道串在一起的情况。如 cmd1 | cmd2 | cmd3 | …这样会让问题变得难以排查,因为中间数据我们都看不到。

如果改成这样的格式:

cmd1 > out1.dat
cat out1 | cmd2 > out2.dat
cat out2 | cmd3 > out3.dat

性能又不太好,因为这样 cmd1, cmd2, cmd3 是串行运行的,这时可以用 tee 命令:

cmd1 | tee out1.dat | cmd2 | tee out2.dat | cmd3 > out3.dat

编写可靠bash脚本的一些技巧相关推荐

  1. 编写可靠 bash 脚本的一些技巧

    作者:astarsun,腾讯搜索工程师 写过很多 bash 脚本的人都知道,bash 的坑不是一般的多.其实 bash 本身并不是一个很严谨的语言,但是很多时候也不得不用.以下总结了一些编写可靠的 b ...

  2. linux同名文件没有自动替换,linux – bash脚本替换文件中出现的所有占位符

    我正在尝试编写一个bash脚本,用一个同名环境变量替换文件中所有出现的占位符.举个例子,如果我有一个像下面这样的文件-- This is an {{VAR1}} {{VAR2}}. It should ...

  3. linux删除文件退出,在Linux中用于在移动或删除文件时使`tail -f`退出的bash脚本

    目前删除,移动或重命名在其上运行tail -f的文件什么都不做,我希望它能够中止.我已经阅读了手册页,似乎-f应该在文件移动时中止,-F将跟随文件,但在Mac OS X上似乎-f和-F是相同的.如何编 ...

  4. bash脚本 文件_如何使用Bash脚本来管理从AWS S3存储桶下载和查看文件

    bash脚本 文件 As you can read in this article, I recently had some trouble with my email server and deci ...

  5. 编写可靠Linux shell脚本的建议

    编写可靠Linux shell脚本的八个建议https://yangxx.net/archives/949 1. 指定bash shell 脚本的第一行,#!之后应该是什么? 如果拿这个问题去问别人, ...

  6. Bash脚本获取自身完整路径的可靠方法

    本文翻译自:Reliable way for a Bash script to get the full path to itself [duplicate] This question alread ...

  7. bashsupport插件_如何用 bash-support 插件将 Vim 编辑器打造成编写 Bash 脚本的 IDE

    IDE(集成开发环境)就是这样一个软件,它为了最大化程序员生产效率,提供了很多编程所需的设施和组件. IDE 将所有开发工作集中到一个程序中,使得程序员可以编写.修改.编译.部署以及调试程序. 在这篇 ...

  8. bash 脚本编写_如何在Bash中编写循环

    bash 脚本编写 人们想要学习Unix shell的一个常见原因是释放批处理的功能. 如果要对多个文件执行某些操作,一种方法是构造一个遍历这些文件的命令. 在编程术语中,这称为执行控制,最常见的示例 ...

  9. GitHub标星1.3W!五分钟带你搞定Linux Bash脚本使用技巧

    原文:https://mp.weixin.qq.com/s/NnpyTOAghr_MXXK5H9twAg 来自:开源最前线(ID:OpenSourceTop)  综合自:https://leanpub ...

最新文章

  1. 2012-4-2 通过MdiParent设置窗体最前
  2. S5PV210之Sate210-F DIY硬件,移植uboot,kernel,android 活动现在已经进入实施阶段吗,欢迎广大网友参与 !...
  3. Uva572(DFS+联通集)
  4. hiho图的联通性(自留)
  5. 自定义键盘码_无线+矮轴≤299?ikbc S200 2.4G 机械键盘测评
  6. 平均值 oj 山东科技大学 c 语言作业
  7. 动态规划(四)--最长公共子序列
  8. 解决git push报错error: failed to push some refs to 的问题
  9. php中的上传全局变量 把全局变量的数组形式变得更简易
  10. dubbo service注解用法_dubbo学习(四)配置dubbo 注解方式配置
  11. 濛濛有感——懂与不懂(一)
  12. GTRD:最全面的人和小鼠转录因子chip_seq数据库
  13. haproxy编译安装以及配置文档介绍
  14. 微信小程序原生的下拉框组件
  15. Freemarker生成word:导出多张图片list、同时导出文字+图片的list
  16. Vue3技术4之watch监视属性、watch时value问题
  17. ​ClinChoice昆翎完成收购北京岐黄;武田与红十字会达成合作;诺诚健华奥布替尼获美国FDA突破性疗法认定 | 医药健闻...
  18. As Error:Failed to find configured root that contains /storage/emulated/0/xxx/xxx/xxx.png
  19. linux容器内核配置,如何在Linux内核Centos下使用Sentinel LDK保护Docker容器实例
  20. Java后端开发常考面试题大全

热门文章

  1. RuoYi-Cloud 部署篇_03(linux环境 Mysql+nginx版本)
  2. npm ERR! cb() never called!
  3. Spring4.x集成xfire1.26 问题汇总
  4. python输入三行数据_3行Python代码就能获取海量数据?
  5. vue 判断同一数组内的值是否一直_vue一些笔记
  6. mysql数据库模型相应解释_数据库事务系列-MySQL跨行事务模型
  7. React之createRef
  8. python 常用模块函数_python函数和常用模块(三),Day5
  9. else 策略模式去掉if_业务复杂=if else?刚来的大神竟然用策略+工厂彻底干掉了他们!...
  10. lua_path环境变量设置linux,ubuntu16.04安装lua环境