当前位置: 首页 > 科技观察

在脚本中使用Bash信号陷阱

时间:2023-03-23 10:52:27 科技观察

无论你的脚本运行成功与否,信号陷阱都能让它顺利结束。shell脚本的启动并不难检测,但是shell脚本的终止检测却并不容易,因为我们无法确定脚本是否会按预期正常结束,或者由于意外错误而失败。记录脚本失败时正在处理的内容很有用,但有时这样做并不方便。Bash中trap命令的存在,正是为了解决这个问题。它可以捕获脚本的终止信号,并以某种预设的方式做出响应。响应失败如果发生错误,可能会导致错误链的发生。在下面的示例脚本中,首先在/tmp中创建一个临时目录,这样就可以在临时目录中进行解包、文件处理等操作,然后以另一种压缩格式打包:#!/usr/bin/envbashCWD=`pwd`TMP=${TMP:-/tmp/tmpdir}##createtmpdirmkdir"${TMP}"##extractfilestotmptarxf"${1}"--directory"${TMP}"##移动到tmpdir并运行commandsspushd"${TMP}"forIMGin*.jpg;做mogrify-verbose-flip-flop"${IMG}"donetar--create--file"${1%.*}".tar*.jpg##回到originpopd##bundlewithbzip2bzip2--compress"${TMP}"/"${1%.*}".tar\--stdout>"${1%.*}".tbz##清理/usr/bin/rm-r/tmp/tmpdir下正常情况下,这个脚本可以正常执行。但是如果存档中的文件是PNG文件而不是预期的JPEG文件,脚本将在中途失败,这时另一个问题出现了:删除临时目录的最后一步没有正确执行。如果手动删除临时目录,是没有效果的,但是如果不手动删除临时目录,下次执行脚本时,又要处理一个已经存在的临时目录,充满了不可预知性剩余文件。一种解决方案是在脚本开头添加预防性删除逻辑来处理这种情况。但是这种做法似乎有点暴力,应该从结构上解决这个问题。使用trap是一种优雅的方式。使用trap捕获信号我们可以使用trap来捕获程序运行时的信号。如果您使用过kill或killall命令,则您使用过一个名为SIGTERM的信号。此外,还可以执行trap-l或trap--list命令列出其他更多信号:$trap--list1)SIGHUP2)SIGINT3)SIGQUIT4)SIGILL5)SIGTRAP6)SIGABRT7)SIGBUS8)SIGFPE9)SIGKILL10)SIGUSR111)SIGSEGV12)SIGUSR213)SIGPIPE14)SIGALRM15)SIGTERM16)SIGSTKFLT17)SIGCHLD18)SIGCONT19)SIGSTOP20)SIGTSTP21)SIGTTIN22)SIGX2FS24)SIGVITALMPRO)29)SIGIO30)SIGPWR31)SIGSYS34)SIGRTMIN35)SIGRTMIN+136)SIGRTMIN+237)SIGRTMIN+338)SIGRTMIN+439)SIGRTMIN+540)SIGRTMIN+641)SIGRTMIN+6+742)SIGRTMIN+843)SIGRTMIN+944)SIGRTMIN+1045)SIGRTMIN+1146)SIGRTMIN+1247)SIGRTMIN+1348)SIGRTMIN+1449)SIGRTMIN+1550)SIGRTMAX-1451)SIGRTMAX-1352)SIGRTMAX-1253)SIGRTMAX-1154)SIGRTMAX-1055)SIGRTMAX-956)SIGRTMAX-857)SIGRTMAX-758)SIGRTMAX-659)SIGRTMAX-560)SIGRTMAX-461)SIGRTMAX-362)SIGRTMAX-263)SIGRTMAX-164)SIGRTMAXtrap可以识别的信号包括:EXIT:进程退出Thesignalsentwhenexiting:进程退出时发送的信号,状态码非0DEBUG:布尔值,表示调试模式如果要在Bash中实现信号捕获,只需要添加要执行的命令在trap之后,加上需要捕获的信号列表就够了。例如下面的语句可以捕获进程运行时用户按下Ctrl+C组合键发出的SIGINT信号:trap"{echo'TerminatedwithCtrl+C';}"SIGINT因此,脚本中的漏洞以上可以通过trap捕获SIGINT、SIGTERM、进程错误退出、进程正常退出、正确处理临时目录等信号来修复:#!/usr/bin/envbashCWD=`pwd`TMP=${TMP:-/tmp/tmpdir}trap\"{/usr/bin/rm-r"${TMP}";exit255;}"\SIGINTSIGTERMERREXIT##createtmpdirmkdir"${TMP}"tarxf"${1}"--directory"${TMP}"##移动到tmp并运行commandsspushd"${TMP}"forIMGin*.jpg;做mogrify-verbose-flip-flop"${IMG}"donetar--create--file"${1%.*}".tar*.jpg##回到原点popd##ziptarbzip2--compress$TMP/“${1%.*}”。tar\--stdout>"${1%.*}".tbz对于更复杂的函数,也可以使用Bash函数来简化陷阱语句。Bash中的信号陷阱信号陷阱是一个好习惯,它允许脚本正确清理,而不管所有任务是否成功执行,从而使脚本更可靠。随意尝试将信号捕获添加到您的脚本中,看看效果如何。