“Exit Trap” 让你的 Bash 脚本更稳固可靠 | Linux 中国
要做到这一点,秘诀就是 bash 提供的一个叫做 EXIT 的伪信号,你可以 trap 它,当脚本因为任何原因退出时,相应的命令或函数就会执行。-- Aaron Maxwell
有用的原文链接请访问文末的“
原文链接
”获得可点击的文内链接、全尺寸原图和相关文章。致谢编译自 | http://redsymbol.net/articles/bash-exit-traps/
作者 | Aaron Maxwell
译者 | Dot Craft (Dotcra) ?? ?? 共计翻译:
4
篇 贡献时间:431 天有个简单实用的技巧可以让你的 bash 脚本更稳健 -- 确保总是执行必要的收尾工作,哪怕是在发生异常的时候。要做到这一点,秘诀就是 bash 提供的一个叫做 EXIT 的伪信号,你可以
trap
[1]
它,当脚本因为任何原因退出时,相应的命令或函数就会执行。我们来看看它是如何工作的。基本的代码结构看起来像这样:
#
!
/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
[2]
#
下载所有版本的
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) 上工作,要在脚本中创建一个镜像。
(名词解释: 在亚马逊云上的运行的服务器叫“
实例
[3]
”。实例从亚马逊机器镜像Amazon Machine Image创建而来,通常被称为 “AMI” 或 “镜像” 。AMI 相当于某个特殊时间点的服务器快照。)我们可以这样创建一个自定义的 AMI :
☉ 基于一个基准 AMI 运行一个实例(例如,启动一个服务器)。☉ 在实例中手动或运行脚本来做一些修改。☉ 用修改后的实例创建一个镜像。☉ 如果不再需要这个实例,可以将其删除。最后一步
相当重要
。如果你的脚本没有把实例删除掉,它会一直运行并计费。(到月底你的账单让你大跌眼镜时,恐怕哭都来不及了!)如果把 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
[4]
。接下来你可以做任何事情:在实例中安装软件,修改配置文件等,然后为最终版本创建一个镜像。实例会在脚本结束时被删除 -- 即使脚本因错误而提前退出。(请确保实例创建成功后再运行业务代码。)更多应用
这篇文章只讲了些皮毛。我已经使用这个 bash 技巧很多年了,现在还能不时发现一些有趣的用法。你也可以把这个方法应用到你自己的场景中,从而提升你的 bash 脚本的可靠性。
尾注
? 注1. mktemp 的选项 -t 在 Linux 上是可选的,在 OS X 上是必需的。带上此选项可以让你的脚本有更好的可移植性。? 注2. 如果只是为了获取实例 ID ,我们不用创建文件,直接写成 instance=$(ec2-run-instances "$ami" | grep "^INSTANCE" | cut -f 2) 就可以。但把输出写入文件可以记录更多有用信息,便于调试 ,代码可读性也更强。作者简介:美国加利福尼亚旧金山的作家,软件工程师,企业家。
Powerful Python
[5]
的作者,他的blog
[6]
。via:
http://redsymbol.net/articles/bash-exit-traps/
作者:
aaron maxwell
[8]
译者:Dotcra
校对:wxy
本文由
LCTT
原创编译,Linux中国
荣誉推出- 滨州:刷爆朋友圈的八中受伤学生真的需要你的转发助力吗?
- 心理测试:你的手指纹路有几个斗?准确测出你的命运与爱情
- 买房我出的钱,凭啥加你的名字?
- 如果,相遇的最终注定是错过,我该怎样在匆忙中,记住你的模样
- 论吃“香”喝“辣”,你的收藏夹里不能少了这家馆子,现在爱琴海
- 6字度夏法,别让你的多肉植物憋死在高温里!
- 只有正在想你的男人,才会故意“这样”问你,别傻傻听不懂!
- 肝脏科专家表示:你的脸上出现这些征兆,说明你的肝脏出现问题了
- 你的美容针可能有问题!警方侦破3800万假药案
- 一个不爱你的老公,回家后这3种表现很\"明显\",别傻傻骗自己!