Shell trap:捕获和处理信号(附带实例)
trap 可以捕获信号并作出相应的“动作”,而捕获信号之后的“动作”是可以自定义或使用函数进行定义的,常用于在脚本执行过程被中断时完成清理工作。
例如,我们之前编辑的批量删除用户脚本,在脚本运行时可以使用重定向的方式,将查找到的符合条件的用户保存到某文件中,然后对文件中的用户进行删除,并在脚本执行完成后对记录用户的文件进行删除。
但在脚本执行过程中存在一些意外情况,例如,删除用户过程太过缓慢,脚本执行者使用 Ctrl+C 快捷键退出脚本执行。也就是说,在得到了 /root/user_del 文件后,在删除 /root/user_del 文件之前,脚本进程被终止执行,在这种情况下 /root/user_del 文件没有进行正常的删除(清理)工作。
使用 trap 就可以很好地解决此类问题,trap 可以在脚本进程执行期间接收到某些信号,然后完成对应的“动作”。而接收什么信号、完成什么动作,都是可以自定义的,“动作”可以使用函数定义。
我们可以执行 trap -l 来查看系统中的信号:
接下来使用一个实际案例来查看 trap 捕获信号的效果:
但是,为了让脚本能够长时间执行(长时间执行才能有机会发出 SIGINT 信号),还需要在脚本中写入 sleep 睡眠,以此延长脚本执行时间。
脚本执行结果如下:
切换到另一终端执行:
现在,我们切换为执行脚本的终端,通过使用 Ctrl+C 快捷键的方式发出 SIGINT 信号:
例如,我们之前编辑的批量删除用户脚本,在脚本运行时可以使用重定向的方式,将查找到的符合条件的用户保存到某文件中,然后对文件中的用户进行删除,并在脚本执行完成后对记录用户的文件进行删除。
但在脚本执行过程中存在一些意外情况,例如,删除用户过程太过缓慢,脚本执行者使用 Ctrl+C 快捷键退出脚本执行。也就是说,在得到了 /root/user_del 文件后,在删除 /root/user_del 文件之前,脚本进程被终止执行,在这种情况下 /root/user_del 文件没有进行正常的删除(清理)工作。
使用 trap 就可以很好地解决此类问题,trap 可以在脚本进程执行期间接收到某些信号,然后完成对应的“动作”。而接收什么信号、完成什么动作,都是可以自定义的,“动作”可以使用函数定义。
我们可以执行 trap -l 来查看系统中的信号:
[root@localhost ~]# trap -l 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 捕获信号的效果:
[root@localhost ~]# vim /root/trap1.sh #!/bin/bash trap 'mytrap' SIGINT # 定义脚本接收到 SIGINT 信号后执行 mytrap 动作 mytrap() { # 声明 mytrap 函数要执行哪些动作 echo "Exiting and cleaning files!" # 首先输出退出提示 rm -rf /root/user_del # 删除文件,完成清理工作 exit 1 # 定义退出返回值为 1 } grep "/bin/bash" /etc/passwd | awk -F ":" '$3>=1000 {print $1}' > /root/user_del # 确定过滤和截取规则 for i in `cat /root/user_del` # 进行 for 循环,循环删除符合条件的用户 do userdel -r "$i" done sleep 100 # 为了脚本能够长时间执行,sleep 睡眠 100 秒 rm -rf /root/user_del相比之前的批量删除,我们在脚本中添加了使用 trap 接收 SIGINT 信号并执行 mytrap 动作,而 mytrap 动作是我们在脚本中以函数的方式自定义的。在函数中我们定义了要输出提示信息、要删除 user_del 文件、要退出脚本并定义退出返回值。
但是,为了让脚本能够长时间执行(长时间执行才能有机会发出 SIGINT 信号),还需要在脚本中写入 sleep 睡眠,以此延长脚本执行时间。
脚本执行结果如下:
[root@localhost ~]# /root/trap1.sh # 在脚本执行后,因为存在 sleep 睡眠,所以脚本执行时间较长且当前屏幕没有任何输出 #现在我们切换其他终端查看 /root/user_del 文件是否存在
切换到另一终端执行:
[root@localhost ~]# ls -l /root/user_del -rw-r--r--. 1 root root 5 Mar 22 09:50 /root/user_del # 文件存在
现在,我们切换为执行脚本的终端,通过使用 Ctrl+C 快捷键的方式发出 SIGINT 信号:
[root@localhost ~]# /root/trap1.sh ^CExiting and cleaning files! # 使用 Ctrl+C 快捷键向脚本发出 SIGINT 信号 [root@localhost ~]# # 脚本执行结束 [root@localhost ~]# echo $? 1 # 查看退出返回值 [root@localhost ~]# ls -l /root/user_del ls: cannot access /root/user_del: No such file or directory # 查看 /root/user_del 文件是否存在在设置了 trap 捕获信号和对应的执行动作之后,我们在脚本执行期间再次执行发出 SIGINT 信号,就会发现 trap 捕获信号后执行了 mytrap 函数中的动作。