bash 抓捕异常_在脚本中使用Bash信号捕获应对异常

无论你的脚本是否成功运行,信号捕获trap都能让它平稳结束。

Shell 脚本的启动并不难被检测到,但 Shell 脚本的终止检测却并不容易,因为我们无法确定脚本会按照预期地正常结束,还是由于意外的错误导致失败。当脚本执行失败时,将正在处理的内容记录下来是非常有用的做法,但有时候这样做起来并不方便。而Bash中trap命令的存在正是为了解决这个问题,它可以捕获到脚本的终止信号,并以某种预设的方式作出应对。

响应失败

如果出现了一个错误,可能导致发生一连串错误。下面示例脚本中,首先在/tmp中创建一个临时目录,这样可以在临时目录中执行解包、文件处理等操作,然后再以另一种压缩格式进行打包:

#!/usr/bin/env bash

CWD=`pwd`

TMP=${TMP:-/tmp/tmpdir}

## create tmp dir

mkdir "${TMP}"

## extract files to tmp

tar xf "${1}" --directory "${TMP}"

## move to tmpdir and run commands

pushd "${TMP}"

for IMG in *.jpg; do

mogrify -verbose -flip -flop "${IMG}"

done

tar --create --file "${1%.*}".tar *.jpg

## move back to origin

popd

## bundle with bzip2

bzip2 --compress "${TMP}"/"${1%.*}".tar \

--stdout > "${1%.*}".tbz

## clean up

/usr/bin/rm -r /tmp/tmpdir

一般情况下,这个脚本都可以按照预期执行。但如果归档文件中的文件是 PNG 文件而不是期望的 JPEG 文件,脚本就会在中途失败,这时候另一个问题就出现了:最后一步删除临时目录的操作没有被正常执行。如果你手动把临时目录删掉,倒是不会造成什么影响,但是如果没有手动把临时目录删掉,在下一次执行这个脚本的时候,它必须处理一个现有的临时目录,里面充满了不可预知的剩余文件。

其中一个解决方案是在脚本开头增加一个预防性删除逻辑用来处理这种情况。但这种做法显得有些暴力,而我们更应该从结构上解决这个问题。使用trap是一个优雅的方法。

使用 trap 捕获信号

我们可以通过trap捕捉程序运行时的信号。如果你使用过kill或者killall命令,那你就已经使用过名为SIGTERM的信号了。除此以外,还可以执行trap -l或trap --list命令列出其它更多的信号:

$ trap --list

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP

6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1

11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM

16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ

26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR

31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3

38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8

43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13

48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12

53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7

58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2

63) SIGRTMAX-1 64) SIGRTMAX

可以被trap识别的信号除了以上这些,还包括:

EXIT:进程退出时发出的信号

ERR:进程以非 0 状态码退出时发出的信号

DEBUG:表示调试模式的布尔值

如果要在 Bash 中实现信号捕获,只需要在trap后加上需要执行的命令,再加上需要捕获的信号列表就可以了。

例如,下面的这行语句可以捕获到在进程运行时用户按下Ctrl + C组合键发出的SIGINT信号:

trap "{ echo 'Terminated with Ctrl+C'; }" SIGINT

因此,上文中脚本的缺陷可以通过使用trap捕获SIGINT、SIGTERM、进程错误退出、进程正常退出等信号,并正确处理临时目录的方式来修复:

#!/usr/bin/env bash

CWD=`pwd`

TMP=${TMP:-/tmp/tmpdir}

trap \

"{ /usr/bin/rm -r "${TMP}" ; exit 255; }" \

SIGINT SIGTERM ERR EXIT

## create tmp dir

mkdir "${TMP}"

tar xf "${1}" --directory "${TMP}"

## move to tmp and run commands

pushd "${TMP}"

for IMG in *.jpg; do

mogrify -verbose -flip -flop "${IMG}"

done

tar --create --file "${1%.*}".tar *.jpg

## move back to origin

popd

## zip tar

bzip2 --compress $TMP/"${1%.*}".tar \

--stdout > "${1%.*}".tbz

对于更复杂的功能,还可以用Bash 函数来简化trap语句。

Bash 中的信号捕获

信号捕获可以让脚本在无论是否成功执行所有任务的情况下都能够正确完成清理工作,能让你的脚本更加可靠,这是一个很好的习惯。尽管尝试把信号捕获加入到你的脚本里看看能够起到什么作用吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值