“Exit Trap” 让你的 Bash 脚本更稳固可靠
创始人
2024-03-01 22:57:57
0

有个简单实用的技巧可以让你的 bash 脚本更稳健 – 确保总是执行必要的收尾工作,哪怕是在发生异常的时候。要做到这一点,秘诀就是 bash 提供的一个叫做 EXIT 的伪信号,你可以 trap 它,当脚本因为任何原因退出时,相应的命令或函数就会执行。我们来看看它是如何工作的。

基本的代码结构看起来像这样:

#!/bin/bash
function finish {
  # 你的收尾代码
}
trap finish EXIT

你可以把任何你觉得务必要运行的代码放在这个 finish 函数里。一个很好的例子是:创建一个临时目录,事后再删除它。

#!/bin/bash
scratch=$(mktemp -d -t tmp.XXXXXXXXXX)
function finish {
  rm -rf "$scratch"
}
trap finish EXIT

这样,在你的核心代码中,你就可以在这个 $scratch 目录里下载、生成、操作中间或临时数据了。 注1

# 下载所有版本的 linux 内核…… 为了科学研究!
for major in {1..4}; do
  for minor in {0..99}; do
    for patchlevel in {0..99}; do
      tarball="linux-${major}-${minor}-${patchlevel}.tar.bz2"
      curl -q "http://kernel.org/path/to/$tarball" -o "$scratch/$tarball" || true
      if [ -f "$scratch/$tarball" ]; then
        tar jxf "$scratch/$tarball"
      fi
    done
  done
done
# 整合成单个文件
# 复制到目标位置
cp "$scratch/frankenstein-linux.tar.bz2" "$1"
# 脚本结束, scratch 目录自动被删除

比较一下如果不用 trap ,你是怎么删除 scratch 目录的:

#!/bin/bash
# 别这样做!

scratch=$(mktemp -d -t tmp.XXXXXXXXXX)

# 在这里插入你的几十上百行代码

# 都搞定了,退出之前把目录删除
rm -rf "$scratch"

这有什么问题么?很多:

  • 如果运行出错导致脚本提前退出, scratch 目录及里面的内容不会被删除。这会导致资料泄漏,可能引发安全问题。
  • 如果这个脚本的设计初衷就是在脚本末尾以前退出,那么你必须手动复制粘贴 rm 命令到每一个出口。
  • 这也给维护带来了麻烦。如果今后在脚本某处添加了一个 exit ,你很可能就忘了加上删除操作 – 从而制造潜在的安全漏洞。

无论如何,服务要在线

另外一个场景: 想象一下你正在运行一些自动化系统运维任务,要临时关闭一项服务,最后这项服务需要重启,而且要万无一失,即使脚本运行出错。那么你可以这样做:

function finish {
    # 重启服务
    sudo /etc/init.d/something start
}
trap finish EXIT
sudo /etc/init.d/something stop
# 主要任务代码

# 脚本结束,执行 finish 函数重启服务

一个具体的实例:比如 Ubuntu 服务器上运行着 MongoDB ,你要为 crond 写一个脚本来临时关闭服务并做一些日常维护工作。你应该这样写:

function finish {
    # 重启服务
    sudo service mongdb start
}
trap finish EXIT
# 关闭 mongod 服务
sudo service mongdb stop
# (如果 mongod 配置了 fork ,比如 replica set ,你可能需要执行 “sudo killall --wait /usr/bin/mongod”)

控制开销

有一种情况特别能体现 EXIT trap 的价值:如果你的脚本运行过程中需要初始化一下成本高昂的资源,结束时要确保把它们释放掉。比如你在 AWS (Amazon Web Services) 上工作,要在脚本中创建一个镜像。

(名词解释: 在亚马逊云上的运行的服务器叫“实例”。实例从 亚马逊机器镜像 Amazon Machine Image 创建而来,通常被称为 “AMI” 或 “镜像” 。AMI 相当于某个特殊时间点的服务器快照。)

我们可以这样创建一个自定义的 AMI :

  1. 基于一个基准 AMI 运行一个实例(例如,启动一个服务器)。
  2. 在实例中手动或运行脚本来做一些修改。
  3. 用修改后的实例创建一个镜像。
  4. 如果不再需要这个实例,可以将其删除。

最后一步相当重要。如果你的脚本没有把实例删除掉,它会一直运行并计费。(到月底你的账单让你大跌眼镜时,恐怕哭都来不及了!)

如果把 AMI 的创建封装在脚本里,我们就可以利用 trap EXIT 来删除实例了。我们还可以用上 EC2 的命令行工具:

#!/bin/bash
# 定义基准 AMI 的 ID
ami=$1
# 保存临时实例的 ID
instance=''
# 作为 IT 人,让我们看看 scratch 目录的另类用法
scratch=$(mktemp -d -t tmp.XXXXXXXXXX)
function finish {
    if [ -n "$instance" ]; then
        ec2-terminate-instances "$instance"
    fi
    rm -rf "$scratch"
}
trap finish EXIT
# 创建实例,将输出(包含实例 ID )保存到 scratch 目录下的文件里
ec2-run-instances "$ami" > "$scratch/run-instance"
# 提取实例 ID
instance=$(grep '^INSTANCE' "$scratch/run-instance" | cut -f 2)

脚本执行到这里,实例(EC2 服务器)已经开始运行 注2 。接下来你可以做任何事情:在实例中安装软件,修改配置文件等,然后为最终版本创建一个镜像。实例会在脚本结束时被删除 – 即使脚本因错误而提前退出。(请确保实例创建成功后再运行业务代码。)

更多应用

这篇文章只讲了些皮毛。我已经使用这个 bash 技巧很多年了,现在还能不时发现一些有趣的用法。你也可以把这个方法应用到你自己的场景中,从而提升你的 bash 脚本的可靠性。

尾注

  • 注1. mktemp 的选项 -t 在 Linux 上是可选的,在 OS X 上是必需的。带上此选项可以让你的脚本有更好的可移植性。
  • 注2. 如果只是为了获取实例 ID ,我们不用创建文件,直接写成 instance=$(ec2-run-instances "$ami" | grep '^INSTANCE' | cut -f 2) 就可以。但把输出写入文件可以记录更多有用信息,便于调试 ,代码可读性也更强。

作者简介:美国加利福尼亚旧金山的作家,软件工程师,企业家。Powerful Python 的作者,他的 blog


via: http://redsymbol.net/articles/bash-exit-traps/

作者:aaron maxwell 译者:Dotcra 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

AI辅助Linux系统管理...
我并非所有场景都推崇使用AI。在创意类工作中,我坚持认为AI介入空...
2026-06-20 12:53:31
北大团队首创"电...
这项由北京大学、快手科技、华南理工大学、电子科技大学、香港大学以及...
2026-03-13 19:48:44
基础Ubuntu网络代理的...
Ubuntu系统的网络代理配置是许多用户日常使用中的必备技能,从基...
2025-08-24 16:46:06
开启“零门槛”AI视频创作...
随着移动端的普及和数字化技术的成熟,短视频创作内容不仅逐渐向各行各...
2025-07-11 14:12:34
编写CentOS系统下的B...
面对重复繁琐的服务器操作,手动输入命令效率低下且易出错。为了解决这...
2025-06-15 08:43:24
怎么用AI做短视频赚钱?小...
嘿,你是不是也刷到过那些爆火的短视频,心想:“这玩意儿咋这么赚钱?...
2025-06-06 22:12:18

热门资讯

“Linux 中国” 开源社区... 这是一个艰难的决定 —— 这不是调侃,而是当你真正做出改变了你十几年生活轨迹的决定时,你就会明白这个...
硬核观察 #1263 德国铁路... #1 德国铁路公司招聘 Windows for Workgroups 3.11 管理员该职位要求了解...
人工智能教程(七):Sciki... 在本系列的 上一篇文章 中,我们用 TensorFlow 构建了第一个神经网络,然后还通过 Kera...
关闭 WSL 中正在运行的 L... 以下是使用 WSL 关闭在 Windows 内运行的 Linux 系统的不同方法。你使用 WSL 在...
MX Linux 23.2 “... 最新的 MX Linux 23.2 小版本更新带来了基本的改进和新增功能。MX Linux 推出了 ...
硬核观察 #1262 网络上最... #1 网络上最古老的软件存档库之一即将关闭美国新墨西哥州立大学(NMSU)最近宣布,即将于 2024...
如何在 RHEL 上通过绑定配... 在 Linux 中,你可以根据自己的需要创建高级网络设置,如 网络绑定 Network Bondin...
Linux Matrix 消息... Matrix 用户可以升级 Fractal 6 了!Fractal 是目前 最好的 Matrix 客...
如何通过 VLC 使用字幕 使用 VLC 媒体播放器播放和管理字幕的新手指南。我是一个超级动漫迷,如果你想观看最新的剧集,那么你...
硬核观察 #1261 X 计划... #1 X 计划在奥斯汀建立内容审核总部据报道,该部门将招聘 100 名全职员工,将主要关注 CSE ...