vieyahn2017 / shellv

shell command test and study
4 stars 1 forks source link

5.11 shell编程之trap #38

Open vieyahn2017 opened 5 years ago

vieyahn2017 commented 5 years ago

shell编程之trap

vieyahn2017 commented 5 years ago

trap可以使你在脚本中捕捉信号,该命令的一般形式为:

trap command signal(s)

捕捉信号,实现类似回调函数的功能

vieyahn2017 commented 5 years ago

Shell编程之trap命令

https://www.jianshu.com/p/6cffb6f3d928

今天看公司里面的一段代码,看到下面一段:

trap "${log_cmds}" EXIT

当时看到这段以为这个trap是自己写的一个函数,搜索了一下才发现这个居然是linux自带的一个命令,了解之后才发现这个命令的功能真是太强大了。实在是值得我好好写一篇文章记载下来。

用法

到底是什么功能惊艳到我呢?trap是一个可以设置接收信号类型,然后执行你给出的命令的命令。比如上面我贴的代码,放在脚本里面的含义就是当脚本执行完毕,执行中间的命令。这种类似回调函数和Go语言中defer一样的用法,没想到Shell里面也有。具体的用法就是:

trap command signal

其中中间的是我们需要执行的命令,最后的是我们想要监听的信号(这里就不列出了所有的信号,可以trap -l查看), 这样以后如果需要在脚本执行完毕后加log,就可以这么操作。 例子

trap "echo 'xxoo' " HUP INT QUIT TSTP

执行完上面命令,每次在控制台Ctrl+c都会打印"xx00",这样我们可以做到屏蔽信号的作用,恢复到原样,执行下面的命令:

trap : HUP INT QUIT TSTP

关于Linux的命令和Shell编程真是博大精深,需要好好去学习,去从别人的代码中去学习。

vieyahn2017 commented 5 years ago

来自 (Shell脚本调试技巧 #26)的例子:

#!/bin/sh

errTrap ()
{
    echo "[LINE:$1] ERROR: Command or function exited with status $?"
}

foo ()
{
    return 1;
}

trap 'errTrap $LINENO' ERR
notExisted
foo

执行:

$ sh trap1.sh
trap1.sh: line 14: notExisted: command not found
[LINE:14] ERROR: Command or function exited with status 127
[LINE:10] ERROR: Command or function exited with status 1
vieyahn2017 commented 5 years ago

shell编程之trap

https://blog.csdn.net/yuesichiu/article/details/8600315

trap可以使你在脚本中捕捉信号,该命令的一般形式为:

trap command signal(s)

其中,command是被捕捉到信号后所采取的一序列操作。 实际使用中,command一般是一个专门用来处理所捕捉到的信号的函数。 command需要用双引号引起来,signal就是待捕捉的信号。

脚本在捕捉到一个信号后,通常会采取某些行动。最常见的动作包括:

1)清除临时文件 2)忽略该信号 3)询问用户是否终止该脚本的运行。

常用的信号包括1,2,3,15

1----SIGHUP   挂起或父进程被杀死

2----SIGINT     来自键盘的中断<CTRL + C>

3----SIGQUIT   从键盘退出

15----SIGTERM 软终止

实例:trap.sh

#!/bin/sh
trap "my_exit" EXIT

my_exit()
{
    echo -e "\nReceived interrupt..."
    echo "Do you wish to really exit ??"
    echo " Y: Yes"
    echo " N: No"
    echo -n "Your choice [Y..N] >"
    read ANS
    case $ANS in
        Y|y) exit 1;;
        N|n);;
    esac
}
echo -n "Enter your name: "
read NAME
echo -n "Enter your age: "
read AGE

在脚本捕捉到信号2后将会向用户提供一个选择,询问用户是否真的要退出。

执行效果

$ sh trap.sh
Enter your name: q
Enter your age: 1

Received interrupt...
Do you wish to really exit ??
 Y: Yes
 N: No
Your choice [Y..N] >y

$ echo $?
1

$ sh trap.sh
Enter your name: q
Enter your age: 1

Received interrupt...
Do you wish to really exit ??
 Y: Yes
 N: No
Your choice [Y..N] >n

$ echo $?
0
vieyahn2017 commented 5 years ago

我使用过的Linux命令之trap - 在脚本中处理信号

https://blog.csdn.net/codingstandards/article/details/83776213

用途说明 trap是一个shell内建命令,它用来在脚本中指定信号如何处理。 比如,按Ctrl+C会使脚本终止执行,实际上系统发送了SIGINT信号给脚本进程,SIGINT信号的默认处理方式就是退出程序。 如果要在Ctrl+C不退出程序,那么就得使用trap命令来指定一下SIGINT的处理方式了。 trap命令不仅仅处理Linux信号,还能对脚本退出(EXIT)、调试(DEBUG)、错误(ERR)、返回(RETURN)等情况指定处理方式。

vieyahn2017 commented 5 years ago

根据上文启发写的例子

比如有这样一个脚本:

#!/bin/sh

for i in `seq 0 60`; do
    echo sleeping, when i=$i
    sleep 1
done

会一直循环直到结束,按Ctrl+C会使脚本终止执行,(实际上系统发送了SIGINT信号给脚本进程)。

可以自定义trap,让程序真正不被中止。

#!/bin/sh

no_interrupt () 
{
    echo "Ctrl+C is forbidden."
}

trap "no_interrupt" INT

for i in `seq 0 10`; do
    echo sleeping, when i=$i
    sleep 1
done

执行效果:

$ sh trap2.sh
sleeping, when i=0
sleeping, when i=1
sleeping, when i=2
Ctrl+C is forbidden.
sleeping, when i=3
sleeping, when i=4
...
vieyahn2017 commented 5 years ago

格式:trap "commands" signals

当shell接收到signals指定的信号时,执行commands命令。(The command arg is to be read and executed when the shell receives signal(s) sigspec. )

格式:trap signals

如果没有指定命令部分,那么就将信号处理复原。比如 trap INT 就表明恢复Ctrl+C退出。(If arg is absent (and there is a single sigspec) or -, each specified signal is reset to its original disposition (the value it had upon entrance to the shell). )

格式:trap "" signals

忽略信号signals,可以多个,比如 trap "" INT 表明忽略SIGINT信号,按Ctrl+C也不能使脚本退出。又如 trap "" HUP 表明忽略SIGHUP信号,即网络断开时也不能使脚本退出。(If arg is the null string the signal specified by each sigspec is ignored by the shell and by the commands it invokes. )

格式:trap -p

格式:trap -p signal

把当前的trap设置打印出来。(If arg is not present and -p has been supplied, then the trap commands associated with each sigspec are displayed. If no arguments are supplied or if only -p is given, trap prints the list of commands associated with each signal.)

格式:trap -l

把所有信号打印出来。(The -l option causes the shell to print a list of signal names and their corresponding numbers. Each sigspec is either a signal name defined in , or a signal number. Signal names are case insensitive and the SIG prefix is optional.)

格式:trap "commands" EXIT

脚本退出时执行commands指定的命令。(If a sigspec is EXIT (0) the command arg is executed on exit from the shell.)

格式:trap "commands" DEBUG

在脚本执行时打印调试信息,比如打印将要执行的命令及参数列表。(If a sigspec is DEBUG, the command arg is executed before every simple command, for command, case command, select command, every arithmetic for command, and before the first command executes in a shell function (see SHELL GRAMMAR above). Refer to the description of the extdebug option to the shopt builtin for details of its effect on the DEBUG trap.)

格式:trap "commands" ERR

当命令出错,退出码非0,执行commands指定的命令。(If a sigspec is ERR, the command arg is executed whenever a simple command has a non-zero exit status, subject to the following conditions. The ERR trap is not executed if the failed command is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of a && or ┅Ι│ list, or if the command’s return value is being inverted via !. These are the same conditions obeyed by the errexit option.)

格式:trap "commands" RETURN

当从shell函数返回、或者使用source命令执行另一个脚本文件时,执行commands指定的命令。(If a sigspec is RETURN, the command arg is executed each time a shell function or a script executed with the . or source builtins finishes executing. Signals ignored upon entry to the shell cannot be trapped or reset. Trapped signals that are not being ignored are reset to their original values in a child process when it is created. The return status is false if any sigspec is invalid; otherwise trap returns true.)

vieyahn2017 commented 5 years ago

测试

$ trap -p
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

$ func()
{
    echo 1
}

$ trap "func" INT

$ trap -p INT
trap -- 'func' SIGINT

$ trap -p
trap -- 'func' SIGINT
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

$ trap INT

$ trap -p
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU
vieyahn2017 commented 5 years ago

示例三 在脚本中使用 下面的脚本用于检查和启动java程序。

第7行:找出正在运行的符合指定特征的进程;

第11行:如果找到了这样的进程,就杀掉;

第22行:以后台方式启动java程序;

第24行:得到刚启动的程序的pid;

第28行:对SIGTERM信号设置处理方式:结束启动的java程序;

第30行:等待后台进程结束。

#!/bin/sh

#2007.05.06/07
# 增加了杀掉LAST_PID功能
# 增加了脚本退出时杀掉THIS_PID功能

LAST_PID=$(ps -ef|grep 'java.*Zhenjiang'|grep -v grep|awk '{print $2}')

echo "LAST_PID=$LAST_PID"

if [ -n "$LAST_PID" ] &&  [ "$LAST_PID" -gt 0 ]; then
    echo "LAST PROCESS NOT EXIT, NOW KILL IT!"
    kill $LAST_PID
    sleep 1
fi

if ! cd ../opt/zhenjiang; then
    echo "CHANGE DIRECTORY FAILED"
    exit 1
fi

java -classpath .:./cpinterfaceapi.jar:./log4j-1.2.14.jar:./hyjc.jar:./smj.client.jar Zhenjiang &

THIS_PID=$!

echo "THIS_PID=$THIS_PID"

trap "kill $THIS_PID" TERM

wait