使用trap为shell的信号设置陷阱和陷阱运行原理以及如何复原默认信号处理

2021/7/18 7:09:40

本文主要是介绍使用trap为shell的信号设置陷阱和陷阱运行原理以及如何复原默认信号处理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

陷阱信号

当你的程序运行时,按下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


这篇关于使用trap为shell的信号设置陷阱和陷阱运行原理以及如何复原默认信号处理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程