进程(七):trap 语句

Bash 的内部命令 trap,让我们可以在 Shell 脚本内捕获特定的信号并对它们进行处理。

trap 命令的语法如下所示:

trap command signal [ signal ... ]

上述语法中,command 可以是一个脚本或是一个函数。signal 既可以用信号名,也可以用信号值指定。我们可以不指定任何参数,而直接使用 trap 命令,它将会打印与每个要捕获的信号相关联的命令的列表。

当 Shell 收到信号 signal(s) 时,command 将被读取和执行。比如,如果 signal 是 0 或 EXIT 时,command 会在 Shell 退出时被执行。如果 signal 是 DEBUG 时,command 会在每个命令后被执行。signal 也可以被指定为 ERR,那么每当一个命令以非 0 状态退出时,command 就会被执行(注意,当非 0 退出状态来自一个 if 语句部分,或来自 while、until 循环时,command 不会被执行)。

当调试较大的脚本时,我们可能想要赋予某个变量一个踪迹属性,并捕获变量的调试信息。通常,我们可能只使用一个简单的赋值语句,比如,VARIABLE=value,来定义一个变量。如果使用类似如下的语句替代上述的变量定义,可能会为你提供更有用的调试信息:

# 声明变量 VARIABLE,并赋予其踪迹属性
declare -t VARIABLE=value
# 捕获 DEBUG
trap "echo VARIABLE is being used here." DEBUG

# 脚本的余下部分

现在,我们创建一个名为 testtrap1.sh 的脚本,其内容如下所示:

#! /bin/bash

# 捕获退出状态 0
trap 'echo "Exit 0 signal detected..."' 0

# 打印信息
echo "This script is used for testing trap command."

# 以状态(信号)0 退出此 Shell 脚本
exit 0

上述脚本的运行结果:

运行结果

我们再创建一个名称为 testtrap2.sh 的脚本,其内容类似如下所示:

#! /bin/bash

# 捕获信号 SIGINT,然后打印相应的信息
trap "echo 'You hit Ctrl + C! I am ignoring you.'" SIGINT

# 捕获信号 SIGTERM,然后打印相应信息
trap "echo 'You tried to kill me! I am ignoring you.'" SIGTERM

# 循环 5 次
for i in {1..5}; do
    echo "Iteration $i of 5"
    sleep 5
done
运行结果

有时,接收到一个信号后你可能不想对其做任何处理。比如,当你的脚本处理较大的文件时,你可能希望阻止一些错误地输入 Ctrl+C 或 Ctrl+\ 组合键的做法,并且希望它能执行完成而不被用户中断。这时就可以使用空字符串(" " 或' ')作为 trap 的命令参数,那么 Shell 将忽略这些信号。其用法类似如下所示:

trap ' ' SIGHUP SIGINT [ signals ... ]

信号多用于以友好的方式结束一个进程的执行,即允许进程在退出之前有机会做一些清理工作。然而,信号同样还可用于其他用途。例如,当终端窗口的大小改变时,在此窗口中运行的 Shell 都会接收到信号 SIGWINCH。通常,这个信号是被忽略的,但是,如果一个程序关心窗口大小的变化,它就可以捕获这个信号,并用特定的方式处理它。

** 注意: **
除 SIGKILL 信号以外,其他任何信号都可以被捕获并通过调用 C 函数 signal 处理。

捕获并处理 SIGWINCH 信号实例:

#! /bin/bash

echo "Adjust the size of your window now."

trap "echo Window size changed." SIGWINCH

COUNT=0

while [ $COUNT -lt 30 ] ; do
        COUNT=$(($COUNT + 1))
        sleep 1
done

上述实例在运行时,我们改变命令行程序的窗口大小的运行结果如下:

运行结果

接收到退出信号后清理程序的实例:

#! /bin/bash

trap 'my_exit; exit' SIGINT SIGQUIT

# 用户调用 kill -1 PID 命令
trap 'echo Going down on a SIGHUP - signal 1, no exiting...; exit' SIGHUP

count=0

tmp_file=`mktemp /tmp/file.$$.XXXXXX`

my_exit()
{
        echo "You hit Ctrl-C/Ctrl-\, now exiting..."
        rm -f $tmp_file >& /dev/null
}

echo "Do something..." > $tmp_file

# 执行无限循环
while :
do
        sleep 1
        count=$(expr $count + 1)
        echo $count
done

当上述脚本运行时,接收到 SIGINT 或 SIGQIUT 信号后会调用 my_exit 函数清理临时文件之后退出。这个实例的具体使用效果,大家可以自行尝试。

Bash 中有两个内部变量可以方便地处理信号时,为我们提供更多的与脚本终结相关的信息。这两个变量分别是 LINENO 和 BASH_COMMAND。BASH_COMMAND 是 Bash 中特有的。这两个变量分别用于报告脚本当前执行的行号和脚本当前运行的命令。

LINENO 和 BASH_COMMAND 使用实例:

#! /bin/bash

trap 'my_exit $LINENO $BASH_COMMAND; exit' SIGHUP SIGINT SIGQUIT

my_exit()
{
        echo "$(basename $0) caught error on line : $1 command was: $2"
        logger -p notice "script: $(basename $0) was terminated: line: $1, command was $2"
        # 其他的一些清理命令
}

while :
do
        sleep 1
        count=$(( $count + 1 ))
        echo $count
done
运行结果

** 移除捕获 **

如果我们在脚本中应用了捕获,我们通常会在脚本的结尾处,将接收到信号时的行为处理重置为默认模式,重置(移除)捕获的语法如下所示:

trap - signal [ signal ... ]

从上述语法中可以看出,使用破折号作为 trap 语句的命令参数,就可以移除信号的捕获。

移除捕获实例:

#! /bin/bash

function cleanup() {
        if [[ -e $msgfile ]]; then
                mv $msgfile $msgfile.dead
        fi
        exit
}

trap cleanup INT TERM

msgfile=`mktemp /tmp/testtrap.$$.XXXXXX`

cat > $msgfile

rm $msgfile

trap - INT TERM

本文参考自 《Linux Shell命令行及脚本编程实例详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容

  • 本文全面系统地介绍了shell脚本调试技术,包括使用echo, tee, trap等命令输出关键信息,跟踪变量的值...
    liuzg0734阅读 897评论 0 14
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,626评论 18 139
  • 二. 在shell脚本中输出调试信息 通过在程序中加入调试语句把一些关键地方或出错的地方的相关信息显示出来是最常见...
    很少更新了阅读 820评论 0 1
  • Bash内置基本变量 PWD : 显示当前的工作目录 OLDPWD : 显示上一次的工作目录 ~:用户家目录 - ...
    魏镇坪阅读 1,516评论 0 6
  • 1. “:” 命令 Shell命令总是有一些奇怪的命令符,比如之前看到的“[”;这里又来了一个。你没有看错,“:”...
    姜淑均阅读 579评论 0 0