文章目录

  • Pre
  • 避免重叠运行
  • 意外退出时杀掉所有子进程
  • timeout 限制运行时间
  • 连续管道时, 使用 tee 将中间结果落盘,以便查问题
  • set -x -e -u -o pipefail

Pre

Linux-编写Shell的几个技巧 继续


避免重叠运行

在一些场景中,如果不希望一个脚本有多个实例在同时运行。比如用 crontab 周期性运行脚本时,有时不希望上一个轮次还没运行完,下一个轮次就开始运行了。

这时可以用 flock 命令来解决。 flock 通过文件锁的方式来保证独占运行,并且还有一个好处是进程退出时,文件锁也会自动释放,不需要额外处理。

  • 用法 1: 假设你的入口脚本是 artisan.sh,可以新建一个脚本,通过 flock 来运行它:
# flock --wait 超时时间   -e 锁文件   -c "要执行的命令"
# 例如:
flock  --wait 5  -e "lock_myscript"  -c "bash artisan.sh"
  • 用法 2: 也可以在原有脚本里使用 flock。 可以把文件打开为一个文件描述符,然后使用 flock 对它上锁(flock 可以接受文件描述符参数)。
exec 123<>lock_myscript   # 把lock_myscript打开为文件描述符123
flock  --wait 5  123 || { echo 'cannot get lock, exit'; exit 1; }


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

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

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

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

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


timeout 限制运行时间

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

timeout 600s  some_command arg1 arg2

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

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


连续管道时, 使用 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


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 。 这mmp就死定了啊 . 使用 -u 可以避免这种情况。

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

${SOME_VAR:-}

bash变量展开语法
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

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

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

Linux-编写Shell的几个技巧_02相关推荐

  1. linux——编写Shell脚本常用命令:diff、patch、cut、sort、uniq、、||、test、tr

    diff 和 patch 命令帮助:diff –help | patch –help diff命令在最简单的情况下,比较给定的两个文件的不同.如果使用"-"代替"文件&q ...

  2. linux 脚本监听,Linux—编写shell脚本监控主机

    编写SHELL脚本监控主机 1.用vi命令在/root目录是新建一个脚本文件sysmon.sh: 内容如下: #!/bin/bash dug=$(df -h | grep "/$" ...

  3. linux 命令脚本文件,Linux编写shell脚本执行多个命令

    背景:Linux做项目写完代码之后,需要用一个文件夹下多个文件进行测试,而且需要对于同一个文件执行多个命令,这个时候如果一个一个命令输入比较繁琐,于是写了一个简单的命令脚本如下: #! /bin/ba ...

  4. Linux—编写shell脚本操作数据库执行sql

    修改数据库数据   在升级应用时,我们常常会遇到升级数据库的问题,这就涉及到sql脚本的编写.   一般我们会通过写sql脚本,然后将xxx.sql脚本放到数据库中进行source xxx.sql执行 ...

  5. linux 编写shell脚本

    目录 简单shell 示例 接受用户参数 判断用户参数(判断语法) 流程控制语句 (if .for .while.case) 简单shell 示例 Shell脚本命令的工作方式有两种:交互式和批处理. ...

  6. linux 编写shell管理脚本01。2

    编写并执行shell脚本 1. 建立包含可执行语句的文本文件 允许环境设置 如:"#!/bin/bash" 注释行 "#""#!" 可执行语 ...

  7. linux 编写sh文件,linux编写shell脚本程序one官方

    ----2.配置串口 ----Windows95的串口配置比Windows3.x较为复杂,但其功能更强大,对一般程序可使用CommConfigDialog函数以对话框方式设置波特率.数据位.奇偶校验. ...

  8. could not open input file linux 编写 shell遇到了小问题

    对于linux新人来说,这个问题是天大的.但最后还是被我的耐心给解决掉了. 是这样的 今天学习秒杀知识点,其中需要用到定进任务,所以我就启动了一年前装好的虚拟机.对于linux,本人还是有点基础知的. ...

  9. linux -- 编写shell脚本对磁盘自动分区和自动挂载

    编写目的: 实现运维工作的自动化.智能化.可视化 使用parted分区命令,parted相对于fdisk编写脚本更方便,因为fdisk是交互式的命令 #!/bin/bashnum = $(( fdis ...

最新文章

  1. SQL Server Alwayson 主从数据库账号同步
  2. quartz关闭DBUG日志
  3. CentOS 7 安装Boost 1.67及boost_python
  4. 13岁女孩因发布JavaScript无限循环代码被捕
  5. python异步io 队列_python 学习笔记九 队列,异步IO
  6. 确认!别再相信Python了! 程序员:就你敢说...
  7. php fetch返回false,Php fetch返回字符串而不是布爾值“true / false”值
  8. 二叉树的链表存储与遍历
  9. Cocos2d BMFont解析
  10. Windows Server 2008 R2 主域控制器委派DNS到子域控控制器
  11. Windows 7 正在走 XP 系统的老路
  12. 《ActionScript 3.0基础教程》——2.2 显示队列概述
  13. lambda函数if_现代 C++:Lambda 表达式
  14. kafka--Struct Streaming--mysql案例
  15. 单引号、双引号和不加引号区别
  16. 论文阅读笔记 Word Embeddings A Survey
  17. wifi发射功率各国标准_智能插座背后的两种无线协议——WiFi与ZigBee
  18. GRADS软件初步学习
  19. Db2性能问题:临时表空间太大,导致连不上数据库
  20. 一个完整的数据挖掘项目-纽约市建筑能源之星预测

热门文章

  1. Android:按键响应方式第一种onClick属性,第二种方法接口类,第三种方式匿名内部类,第四种方式Activity
  2. Android CameraSurfaceView在SurfaceView上实现拍照,视频录像
  3. pycharm 中写代码的提示的前符号 p,m ,c,v, f 是什么意思
  4. python 快速排序
  5. jsp获取连接池的实时连接数_PHP进阶教程-实现一个简单的MySQL连接池
  6. 【转载】Few-shot learning(少样本学习)和 Meta-learning(元学习)概述
  7. 机器学习笔记:logistic regression
  8. 文巾解题 178. 分数排名
  9. pytorch 实现transformer
  10. 《数据中台实战》:如何通过标签平台圈出产品高价值用户?