当你的程序运行时,按下Control-C或者Control-, 一旦该信号到达程序就立刻终止运行。但是在很多的时候,你可能并不希望在信号到达的时候,程序就立刻停止运行。而是它能希望忽略这个信号而一直运行,或者在程序退出以前,做一些清除操作。trap命令允许你控制你的程序在收到信号以后的行为。
信号的定义是由一个进程发送给另一个进程的,或者在特定的键按下以后由操作系统发送给进程,又或者在异常情况下发生时,由数字组成的非同步的消息。trap命令告诉shell根据收到的信号以不同的方式终止当前的进程。如果trap命令后面跟着一个用引号引用的命令,则在接收到指定数字后,就执行这个命令。shell总共读取两次命令字符串,一次是在设置trap的时候,一次是在信号达到的时候。如果命令字符串被双引号引用,在第一次trap设置时就执行变量和命令替换。如果是用的单引号引用,那么等到信号到达trap开始执行的时候,才运行变量和命令替换。
格式 trap 'command; command' signal-num trap 'command; command' signal-name
示例一
trap 'rm file; exit 1' 0 1 2 15 trap 'rm file; exit 1' EXIT HUP INT TERM # 两种方式都一样
如果在一个脚本运行过程中,系统接到一个前缀SIG,例如SIGHUP,SIGINT等等。bash允许你在信号上使用象征性名称。例如没有前缀或者用数字作为信号的名称。一个叫做EXIT的或者数字0的伪信号将在shell退出时,导致一个陷阱的触发执行。
详细的信号说明见文档。常见的信号以及它们的数值代号、说明如下:
Signal Value Comment
SIGHUP 1 终止进程,特别是终端退出时,此终端内的进程都将被终止
SIGINT 2 中断进程,几乎等同于sigterm,会尽可能的释放执行clean-up,释放资源,保存状态等(CTRL+C)
SIGQUIT 3 从键盘发出杀死(终止)进程的信号
SIGKILL 9 强制杀死进程,该信号不可被捕捉和忽略,进程收到该信号后不会执行任何clean-up行为,所以资源不会释放,状态不会保存
SIGTERM 15 杀死(终止)进程,几乎等同于sigint信号,会尽可能的释放执行clean-up,释放资源,保存状态等
SIGSTOP 19 该信号是不可被捕捉和忽略的进程停止信息,收到信号后会进入stopped状态
SIGTSTP 20 该信号是可被忽略的进程停止信号(CTRL+Z)
信号复位:trap命令后面跟一个信号或者数字,可以把信号复位为默认动作。一旦调用了函数,函数设置的陷阱可以被调用这个函数的shell识别。同时,在函数外设置的陷阱也可以被函数识别。
示例一
trap 2 or trap INT #为信号2设置默认动作。当按下Ctrl-C就会触发这个信号 # 这样用法并不推荐,可能会产生副作用。 trap - signal-list # 这种方法和上面这种方法效果一样,更加稳妥。
忽略信号:如果trap命令后面跟一对空引号,列表中的信号就会被进程所忽略。
trap " " 1 2 trap " " HUP INT #信号1和2将被shell进行忽略
**陷阱列表:**通过输入trap命令,可以显示陷阱的列表和分配给陷阱的命令清单。
trap的语法格式为:
1.trap [-lp] 2.trap cmd-body signal_list 3.trap ‘’ signal_list 4.trap signal_list 5.trap - signale_list
语法说明:
语法1:-l选项用于列出当前系统支持的信号列表,和"kill -l"一样的作用。
-p选项用于列出当前shell环境下已经布置好的陷阱。
语法2:当捕捉到给定的信号列表中的某个信号时,就执行此处给定cmd-body中的命令。
语法3:命令参数为空字符串,这时shell进程和shell进程内的子进程都会忽略信号列表中的信号。
语法4:省略命令参数,重置陷阱为启动shell时的陷阱。不建议此语法,当给定多个信号时结果会出人意料。
语法5:等价于语法4。
trap不接任何参数和选项时,默认为"-p"。
$ trap 'echo hello world' 2 $ trap # 显示默认设置的陷阱 # trap -- 'shell_session_update' EXIT # trap -- 'echo hello world ' SIGKILL # trap -- ' ' SIGINT
示例一
#!/usr/bin/bash # scriptname: trapping.sh trap 'echo "Control-C will not terminate $0."' INT trap 'echo "Control-\ will not terminate $0."' QUIT trap 'echo "Control-Z will not terminate $0."' TSTP echo "When you are ready to exit, please enter a \"stop.\"" while true do echo "go ahead --->" read if [[ $REPLY == [sS]top ]] # read命令如果没有指定变量,则输入内容默认保存在REPLY中 then break fi done (The output) $ bash trapping.sh #When you are ready to exit, please enter a "stop." #go ahead ---> #w #go ahead ---> #e #go ahead ---> #^ZControl-Z will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^\Control-\ will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #^CControl-C will not terminate trapping.sh. #stop #前面的三个信号都触发了设置的命令,打印出了内容,但是并不会终止脚本运行,直到输入stop终止循环。
**复位信号:**trap命令后面以信号名字或者号码作为参数,可以复位信号为默认动作。当然也可以以trap - siglist的形式来复位信号默认行为。
trap 'trap - 2' 2 # 需要按两次才能终止进程
**函数中的陷阱:**如果使用陷阱处理函数中的信号,一旦函数被激活,它将影响整个脚本。陷阱对于脚本来说是全局的。在下面的例子中,陷阱被设置为忽略中断ctrl-c。要终止这个脚本的循环就只能使用kill命令。它证明了在函数中使用陷阱可能出现不可预测的情况。
#!/usr/bin/bash function trapper { echo "In trapper" trap 'echo "Caught in a trap"' INT } while : #等同于true do echo "In the main script" trapper echo "still in main" sleep 5 done (The output) 执行以上脚本以后的输出: #In the main script #In trapper #still in main #^CCaught in a trap #In the main script #In trapper #still in main #^CCaught in a trap #In the main script #In trapper #still in main