bash命令: trap

trap可以帮助您的脚本干净地结束,不管它们是否成功地运行。很容易检测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。有许多其他信号,shell响应,你可以看到大多数的trap -l(如“列表”):

$ 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来预测。除此之外,trap还能识别:

  • EXIT:当shell进程本身退出时发生
  • ERR:当命令(如tar或mkdir)或内置命令(如pushd或cd)以非零状态完成时发生
  • DEBUG:表示调试模式的布尔值
    要在Bash中设置trap,请使用trap,然后是要执行的命令列表,然后是触发它的信号列表。

例如,这个trap检测一个SIGINT,当一个进程正在运行时,用户按下Ctrl+C发送的信号:

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

带有临时目录问题的示例脚本可以通过检测SIGINT、错误和成功退出的trap来修复:

#!/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

trap对于确保脚本干净地结束非常有用,无论它们是否成功运行。完全依赖自动垃圾收集永远都不安全,所以这是一个普遍的好习惯。尝试在脚本中使用它们,看看它们能做什么!

BASH命令自动补全
用PHP的filter_var函数验证IP地址
Tags:, ,