Merge pull request #19840 from wxy/20200629-Using-Bash-traps-in-your-scripts

PRF&PUB:20200629 Using Bash traps in your scripts
This commit is contained in:
Xingyu.Wang 2020-10-13 18:31:29 +08:00 committed by GitHub
commit e667701b0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,17 +1,18 @@
[#]: collector: (lujun9972) [#]: collector: (lujun9972)
[#]: translator: (HankChow) [#]: translator: (HankChow)
[#]: reviewer: ( ) [#]: reviewer: (wxy)
[#]: publisher: ( ) [#]: publisher: (wxy)
[#]: url: ( ) [#]: url: (https://linux.cn/article-12715-1.html)
[#]: subject: (Using Bash traps in your scripts) [#]: subject: (Using Bash traps in your scripts)
[#]: via: (https://opensource.com/article/20/6/bash-trap) [#]: via: (https://opensource.com/article/20/6/bash-trap)
[#]: author: (Seth Kenlon https://opensource.com/users/seth) [#]: author: (Seth Kenlon https://opensource.com/users/seth)
在脚本中使用 Bash 信号捕获 在脚本中使用 Bash 信号捕获
====== ======
> 无论你的脚本是否成功运行,<ruby>信号捕获<rt>trap</rt></ruby>都能让它平稳结束。 > 无论你的脚本是否成功运行,<ruby>信号捕获<rt>trap</rt></ruby>都能让它平稳结束。
![Hands programming][1] ![](https://img.linux.net.cn/data/attachment/album/202010/13/182135f2nktcrnryncisg8.jpg)
Shell 脚本的启动并不难被检测到,但 Shell 脚本的终止检测却并不容易,因为我们无法确定脚本会按照预期地正常结束,还是由于意外的错误导致失败。当脚本执行失败时,将正在处理的内容记录下来是非常有用的做法,但有时候这样做起来并不方便。而 [Bash][2] 中 `trap` 命令的存在正是为了解决这个问题,它可以捕获到脚本的终止信号,并以某种预设的方式作出应对。 Shell 脚本的启动并不难被检测到,但 Shell 脚本的终止检测却并不容易,因为我们无法确定脚本会按照预期地正常结束,还是由于意外的错误导致失败。当脚本执行失败时,将正在处理的内容记录下来是非常有用的做法,但有时候这样做起来并不方便。而 [Bash][2] 中 `trap` 命令的存在正是为了解决这个问题,它可以捕获到脚本的终止信号,并以某种预设的方式作出应对。
@ -19,22 +20,21 @@ Shell 脚本的启动并不难被检测到,但 Shell 脚本的终止检测却
如果出现了一个错误,可能导致发生一连串错误。下面示例脚本中,首先在 `/tmp` 中创建一个临时目录,这样可以在临时目录中执行解包、文件处理等操作,然后再以另一种压缩格式进行打包: 如果出现了一个错误,可能导致发生一连串错误。下面示例脚本中,首先在 `/tmp` 中创建一个临时目录,这样可以在临时目录中执行解包、文件处理等操作,然后再以另一种压缩格式进行打包:
``` ```
#!/usr/bin/env bash #!/usr/bin/env bash
CWD=`pwd` CWD=`pwd`
TMP=${TMP:-/tmp/tmpdir} TMP=${TMP:-/tmp/tmpdir}
## create tmp dir ## create tmp dir
mkdir $TMP mkdir "${TMP}"
## extract files to tmp ## extract files to tmp
tar xf "${1}" --directory $TMP tar xf "${1}" --directory "${TMP}"
## move to tmpdir and run commands ## move to tmpdir and run commands
pushd $TMP pushd "${TMP}"
for IMG in *.jpg; do for IMG in *.jpg; do
  mogrify -verbose -flip -flop $IMG mogrify -verbose -flip -flop "${IMG}"
done done
tar --create --file "${1%.*}".tar *.jpg tar --create --file "${1%.*}".tar *.jpg
@ -42,22 +42,21 @@ tar --create --file "${1%.*}".tar *.jpg
popd popd
## bundle with bzip2 ## bundle with bzip2
bzip2 --compress $TMP/"${1%.*}".tar \ bzip2 --compress "${TMP}"/"${1%.*}".tar \
      --stdout &gt; "${1%.*}".tbz --stdout > "${1%.*}".tbz
## clean up ## clean up
/usr/bin/rm -r /tmp/tmpdir /usr/bin/rm -r /tmp/tmpdir
``` ```
一般情况下,这个脚本都可以按照预期执行。但如果归档文件中的文件是 PNG 文件而不是期望的 JPEG 文件,脚本就会在中途失败,这时候另一个问题就出现了:最后一步删除临时目录的操作没有被正常执行。如果你手动把临时目录删掉,倒是不会造成什么影响,但是如果没有手动把临时目录删掉,在下一次执行这个脚本的时候,就会在一个残留着很多临时文件的临时目录里执行了 一般情况下,这个脚本都可以按照预期执行。但如果归档文件中的文件是 PNG 文件而不是期望的 JPEG 文件,脚本就会在中途失败,这时候另一个问题就出现了:最后一步删除临时目录的操作没有被正常执行。如果你手动把临时目录删掉,倒是不会造成什么影响,但是如果没有手动把临时目录删掉,在下一次执行这个脚本的时候,它必须处理一个现有的临时目录,里面充满了不可预知的剩余文件
其中一个解决方案是在脚本开头增加一个预防性删除逻辑用来处理这种情况。但这种做法显得有些暴力,而我们更应该从结构上解决这个问题。使用 `trap` 是一个优雅的方法。 其中一个解决方案是在脚本开头增加一个预防性删除逻辑用来处理这种情况。但这种做法显得有些暴力,而我们更应该从结构上解决这个问题。使用 `trap` 是一个优雅的方法。
### 使用 `trap` 捕获信号 ### 使用 trap 捕获信号
我们可以通过 `trap` 捕捉程序运行时的信号。如果你使用过 `kill` 或者 `killall` 命令,那你就已经使用过名为 `SIGTERM` 的信号了。除此以外,还可以执行 `trap -l``trap --list` 命令列出其它更多的信号: 我们可以通过 `trap` 捕捉程序运行时的信号。如果你使用过 `kill` 或者 `killall` 命令,那你就已经使用过名为 `SIGTERM` 的信号了。除此以外,还可以执行 `trap -l``trap --list` 命令列出其它更多的信号:
``` ```
$ trap --list $ trap --list
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP  1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
@ -85,40 +84,38 @@ $ trap --list
例如,下面的这行语句可以捕获到在进程运行时用户按下 `Ctrl + C` 组合键发出的 `SIGINT` 信号: 例如,下面的这行语句可以捕获到在进程运行时用户按下 `Ctrl + C` 组合键发出的 `SIGINT` 信号:
``` ```
`trap "{ echo 'Terminated with Ctrl+C'; }" SIGINT` trap "{ echo 'Terminated with Ctrl+C'; }" SIGINT
``` ```
因此,上文中脚本的缺陷可以通过使用 `trap` 捕获 `SIGINT`、`SIGTERM`、进程错误退出、进程正常退出等信号,并正确处理临时目录的方式来修复: 因此,上文中脚本的缺陷可以通过使用 `trap` 捕获 `SIGINT`、`SIGTERM`、进程错误退出、进程正常退出等信号,并正确处理临时目录的方式来修复:
``` ```
#!/usr/bin/env bash #!/usr/bin/env bash
CWD=`pwd` CWD=`pwd`
TMP=${TMP:-/tmp/tmpdir} TMP=${TMP:-/tmp/tmpdir}
trap \ trap \
 "{ /usr/bin/rm -r $TMP ; exit 255; }" \ "{ /usr/bin/rm -r "${TMP}" ; exit 255; }" \
 SIGINT SIGTERM ERR EXIT SIGINT SIGTERM ERR EXIT
## create tmp dir ## create tmp dir
mkdir $TMP mkdir "${TMP}"
tar xf "${1}" --directory $TMP tar xf "${1}" --directory "${TMP}"
## move to tmp and run commands ## move to tmp and run commands
pushd $TMP pushd "${TMP}"
for IMG in *.jpg; do for IMG in *.jpg; do
  mogrify -verbose -flip -flop $IMG mogrify -verbose -flip -flop "${IMG}"
done done
tar --create --file "${1%.*}".tar *.jpgh tar --create --file "${1%.*}".tar *.jpg
## move back to origin ## move back to origin
popd popd
## zip tar ## zip tar
bzip2 --compress $TMP/"${1%.*}".tar \ bzip2 --compress $TMP/"${1%.*}".tar \
      --stdout &gt; "${1%.*}".tbz --stdout > "${1%.*}".tbz
``` ```
对于更复杂的功能,还可以用 [Bash 函数][3]来简化 `trap` 语句。 对于更复杂的功能,还可以用 [Bash 函数][3]来简化 `trap` 语句。
@ -134,7 +131,7 @@ via: https://opensource.com/article/20/6/bash-trap
作者:[Seth Kenlon][a] 作者:[Seth Kenlon][a]
选题:[lujun9972][b] 选题:[lujun9972][b]
译者:[HankChow](https://github.com/HankChow) 译者:[HankChow](https://github.com/HankChow)
校对:[校对者ID](https://github.com/校对者ID) 校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出