何时声明陷阱语句

要在 shell 脚本执行期间的任何时间捕获信号,请在脚本开始处定义 trap 语句。
或者,要仅在要执行某些命令行时捕获信号,我们可以在适当的命令行之前打开 trap 语句,并在命令行执行后关闭 trap 语句。

如果正在使用循环,trap 语句可以包含 continue 语句以使循环从头开始。
我们还可以捕获 EXIT 信号,以便仅在 shell 脚本终止且没有错误时才执行某些语句。
例如,如果 shell 脚本创建了临时文件,我们可以确保使用 EXIT 信号值的陷阱将这些文件删除。

trap 'rm -f /tmp/tfile* ; exit 0' EXIT

注意:前面的示例在陷阱发生时要执行的语句列表中包含一个退出语句。
这是退出脚本所必需的。

以下示例是 /etc/profile 文件的副本,其中添加了一些注释来解释各种陷阱语句。

#ident "@(#)profile 1.18 98/10/03 SMI "/* SVr4.0 1.3 */
# The profile that all logins get before using their own .profile.
trap "" 2 3    # trap INT (Control-C) and QUIT (Control-\)               
               # and give no feedback 
export LOGNAME PATH
if [ "$TERM" = "" ] 
then 
if /bin/i386 
then 
TERM=sun-color 
else 
TERM=sun 
fi 
export TERM
fi
# Login and -su shells get /etc/profile services. 
# -rsh is given its environment in its .profile.
case "
$ cat trapsig2.ksh 
#!/bin/ksh
# Script name: trapsig2.ksh
integer num
exec 2> /dev/null 
trap 'print "You did not enter an integer.\n"' ERR
while (( 1 )) 
do 
       print -n "Enter any number ( -1 to exit ): " 
       read num
       status=$?
       if (( num == -1 ))  
       then 
           break
       elif (( status == 0 ))  
       then 
           print "Square of $num is $(( num * num )). \n" 
       fi 
done
print "Exiting normally"
" in -sh | -ksh | -jsh) if [ ! -f .hushlogin ] then /usr/sbin/quota # Allow the user to break the Message-Of-The-Day only. # The user does this by using Control-C (INT). # Note: QUIT (Control-\) is still trapped (disabled). trap "trap '' 2" 2 /bin/cat -s /etc/motd trap "" 2 # trap Control-C (INT) and give no feedback. /bin/mail -E case $? in 0) echo "You have new mail." ;; 2) echo "You have mail." ;; esac fi esac umask 022 trap 2 3 # Allow the user to terminate with Control-C (INT) or # Control-\(QUIT)

将 trap 语句与 ERR 信号一起使用的示例

以下 trapsig2.ksh 脚本是用 trap 语句、退出状态测试和 exec 语句重写的 traperr1.ksh 脚本,用于将标准错误消息重定向到 /dev/null。
标准错误消息的重定向已完成,以便用户看不到 shell 否则会打印到屏幕上的错误消息。
如果发生错误,我们希望用户只看到我们使用陷阱语句设置的消息。

$?的值在read 语句执行后立即保存在status 变量中,以便稍后检查read 语句是否成功读取整数。 if 语句检查用户是否输入了 -1。如果是,脚本会跳出 while 循环并终止。如果用户没有输入-1并且status的值为0(表示read语句读取一个整数),则打印用户输入的整数的平方,并再次提示用户输入一个数字。

如果用户未输入整数,则 $?的值非零并被捕获。然后执行trap 语句中的print 语句。然后控制返回到 while 循环,提示用户输入另一个数字。示例输出显示了正在执行的脚本,用户输入一个整数,然后是字母 r,另一个整数,最后是 -1 以退出脚本。根据前面的描述,脚本针对用户的相应输入输出的消息是预期的。

$ ./trapsig2.ksh 
Enter any number ( -1 to exit ): 3 
Square of 3 is 9. 
Enter any number ( -1 to exit ): r 
You did not enter an integer.
Enter any number ( -1 to exit ): 8 
Square of 8 is 64. 
Enter any number ( -1 to exit ): -1 
Exiting normally
trap 'action' ERR
on  it road.com

使用 trap 语句捕获用户错误

除了捕捉信号之外,我们还可以使用trap 语句在脚本执行发生错误时采取指定的动作。
这种类型的陷阱语句的语法是:

$ cat traperr1.ksh 
#!/bin/ksh
# Script name: traperr1.ksh
integer num=2
while (( 1 )) 
do
        read num?"Enter any number ( -1 to exit ): "
        if (( num == -1 ))
        then
		break 
		else  
		print "Square of $num is $(( num * num )). \n"
        fi 
done
print "Exiting normally"

$?变量的值指示何时执行陷阱语句。 它保存先前执行的命令或者语句的退出(错误)状态,因此任何非零值都表示错误。 因此,只要$?变为非零,就会执行trap 语句。 如果用户想退出,以下 traperr1.ksh 示例脚本要求用户输入负整数 (-1)。 如果用户输入一个不是 -1 的整数,脚本将打印该数字的平方。 然后脚本请求另一个整数,直到用户通过输入 -1 退出脚本。

示例输出显示了正在执行的脚本。
用户输入字母 r。
这不是整数。
用户的输入被读入变量 num,该变量被声明为只保存整数数据类型。
如果没有 trap 语句,shell 会打印一条错误消息并退出脚本。
我们可以通过使用陷阱语句来避免此问题,如下一节所示。

$ ./traperr1.ksh 
Enter any number (-1 to exit): r 
traperr1.ksh[9]: r: bad number 
Square of 2 is 4.
Enter any number (-1 to exit): -1
trap 'action' signal [ signal2 ... signalx ]

使用 trap 语句捕捉信号

我们可能想要编写一个不希望用户立即使用 Control-C、Control-\ 或者 Control-Z 终止的脚本。
例如,脚本可能需要在退出前进行一些清理。
为了避免在清理完成之前脚本退出,我们可以在脚本中使用 trap 语句来捕获这些信号。

使用陷阱语句的语法是:

trap 'echo "Control-C not available"' INT

其中:

  • action 是一个语句或者多个语句,以分号分隔。如果未提供操作,则不执行任何操作,但信号仍处于“陷阱”状态。将操作括在一对单引号内。
  • signal是要捕获的信号的名称或者编号。

例如,如果要捕获 Control-C 的使用,请使用以下命令:

trap 'echo "Control-C not available" 
echo "Core dumps not allowed" 
sleep 1 
continue' INT QUIT

要执行的语句可以多于一行,只要结束引号后跟要捕获的一个或者多个信号值即可;例如:

kill -9 script_PID
kill -KILL script_PID

如果我们尝试捕获 KILL 或者 STOP 信号,则陷阱不起作用。
shell 不允许我们捕获这两个信号,从而确保我们始终可以终止或者停止进程。
这意味着仍然可以使用以下命令终止 shell 脚本:

trap - signal

此外,可以使用 Control-S 字符暂停 shell 脚本的执行,因为这两个信号永远不会被困在 Korn shell 脚本中。

以下语句告诉 Korn shell 恢复信号的原始操作。

$ cat trapsig.ksh 
#!/bin/ksh
# Script name: trapsig.ksh
trap 'print "Control-C cannot terminate this script."' INT 
trap 'print "Control-\ cannot terminate this script."' QUIT 
trap 'print "Control-Z cannot terminate this script."' TSTP
print "Enter any string (type 'dough' to exit)." 
while (( 1 )) 
do 
       print -n "Rolling..." 
       read string

       if [[ "$string" = "dough" ]]  
       then 
           break  
       fi 
done
print "Exiting normally"

使用 trap 语句的示例

以下 trapsig.ksh 示例脚本为信号 INT (Control-C)、QUIT (Control-) 和 TSTP (Control-Z) 设置陷阱语句。
该脚本没有说明任何实际应用;它只是展示了如何为这些信号安装信号处理。
在trap 语句之后,进入while 循环,打印字符串rolling ... 并等待用户输入。
当用户输入面团字符串时,while 循环和脚本终止。

无法通过键入 Control-C、Control-\ 或者 Control-Z 来终止脚本。
示例输出显示了正在执行的脚本。
用户在第一次滚动...提示后按下回车键。
然后,用户在第二个滚动...提示中键入 d,在第三个提示中键入 s。
用户在下一个滚动...提示中键入 Control-C、Control-\ 和 Control-Z,这会导致执行适当的陷阱语句。

最后,用户输入字符串面团,脚本终止。

$ ./trapsig.ksh 
Enter any string (type 'dough' to exit). 
Rolling... 
Rolling...d 
Rolling...s 
Rolling...^C 
Control-C cannot terminate this script. 
Rolling...^\ 
Control-\ cannot terminate this script. 
Rolling...^Z 
Control-Z cannot terminate this script. 
Rolling...dough 
Exiting normally
$ cat parent 
# trap - SIGNAL - cancel redefinitions 
# trap "cmd" SIGNAL - "cmd" is executed, signal is inherited to child 
# trap : SIGNAL - nothing is executed, signal is inherited to child 
# trap "" SIGNAL - nothing is executed, signal is not inherited to child
#!/bin/ksh 
# 
# Script name: parent
trap "" 3 # ignore, no forward to children 
trap : 2 # ignore, forward to children
./child

以下是使用trap 语句的另一个示例。
此示例使用两个脚本,父脚本和子脚本。
父脚本将信号传递给子脚本,消息来自被调用的脚本。

$ cat child 
# 
# Script name: child
trap 'print "Control-C cannot terminate this script."' INT 
trap 'print "Control-\ cannot terminate this script."' QUIT
while true
 do
      echo "Type ^D to exit..."
      read || break 
done
$ ./parent 
Type ^D to exit... 
^\ 
Type ^D to exit... 
^C
Control-C cannot terminate this script. 
Type ^D to exit...
^\
Type ^D to exit... 
$
# kill -l
EXIT   HUP      INT      QUIT     ILL
TRAP   ABRT     EMT      FPE      KILL
BUS    SEGV     SYS      PIPE     ALRM
TERM   USR1     USR2     CLD      PWR
WINCH  URG      POLL     STOP     TSTP
CONT   TTIN     TTOU     VTALRM   PROF
XCPU   XFSZ     WAITING  LWP      FREEZE
THAW   CANCEL   LOST     RTMIN    RTMIN+1
RTMIN+2 RTMIN+3 RTMAX-3  RTMAX-2  RTMAX-1 
RTMAX

shell信号值

信号是一些异常事件发生的消息或者请求另一个进程做某事的消息。
信号从一个进程发送到另一个进程。
通常,进程向其自己的子进程之一发送信号。
我们可以从信号和终止手册页获取有关信号的更多信息。

我们可以使用 kill 命令向进程发送信号。
root 用户可以向任何进程发送任何信号。
其他用户只能向他们拥有的进程发送信号。
以下是一些可以从键盘发送的信号:

  • 通过键入 Control-C 发送信号 2 (INT)。
  • 通过键入 Control-\ 发送信号 3 (QUIT)。
  • 通过键入 Control-S 发送信号 23 (STOP)。
  • 通过键入 Control-Z 发送信号 24 (TSTP)。
  • 通过键入 Control-Q 发送信号 25 (CONT)。

INT 和 QUIT 信号导致与设备(控制台或者窗口)关联的当前正在运行的进程终止。
TSTP 信号使进程停止或者挂起执行。
作业被置于后台;可以通过将作业置于前台来恢复该过程。
由于发送到执行 shell 脚本的进程的大多数信号都会导致脚本终止,因此下一个主题将介绍如何让脚本避免因指定信号而终止。

Korn shell 有 46 个定义的信号。
每个信号都有一个名称和一个与之相关的编号。
输入“kill -l”命令会显示这个列表:

$ kill -l EXIT 
0
$ kill -l RTMAX 
45
$ kill -l KILL 
9
$ kill -l TSTP 
24

数字信号值的范围从 0 (EXIT) 到 45 (RTMAX)。
我们可以通过执行带有 -l 选项的 kill 命令,后跟信号名称来确认信号的数值;例如:

$ kill -l 0 
EXIT
$ kill -l 45 
RTMAX
$ kill -l 9 
KILL
$ kill -l 24 
TSTP

如果我们知道编号并想了解或者确认信号的名称,请执行带有 -l 选项的 kill 命令,后跟信号编号。

kill -signal pid

我们可以使用 kill 命令向系统上运行的进程发送信号:

kill -9 pid 
kill -KILL pid

其中信号是信号编号或者信号名称,pid 是正在发送信号的进程的进程标识号。
我们可以通过执行 kill 命令并使用 -9 选项来终止进程。
-9 选项向进程发送 KILL 信号。
我们可以使用此处显示的数值或者信号名称。

##代码##

当我们未指定信号名称或者编号时,TERM 信号 Signal 15 将发送到进程。

在 Shell 脚本中使用“trap”捕获信号和处理错误
日期:2020-09-17 00:12:50 来源:oir作者:oir