shell脚本控制——处理信号

Linux利用信号与系统中的进程进行通信。你可以通过对脚本进行编程,使其在收到特定信号时执行某些命令,从而控制shell脚本的操作。

1.重温Linux信号

Linux系统和应用程序可以产生超过30个信号。下表列出了在shell脚本编程时会遇到的最常见的Linux系统信号。 

Linux系统信号
信号描述
1SIGHUP挂起(hang up)进程
2SIGINT中断(interrupt)进程
3SIGQUIT停止(stop)进程
9SIGKILL无条件终止(terminate)进程
15SIGTERM尽可能终止进程
18SIGCONF继续运行停止的进程
19SIGSTOP无条件停止,但不终止进程
20SIGTSTP停止或暂停(pause),但不终止进程

在默认情况下,bash shell会忽略收到的任何SIGQUIT(3)信号和SIGTERM(15)信号(因此交互式shell才不会被意外终止)。但是,bash shell会处理收到的所有SIGHUP(1)信号和SIGINT(2)信号。

如果收到了SIGHUP信号(比如在离开交互式shell时),bash shell就会退出。但在退出之前,它会将SIGHUP信号传给所有由该shell启动的进程,包括正在运行的shell脚本。

随着收到SIGINT信号,shell会被中断。Linux内核将不再为shell分配CPU处理时间。当出现这种情况时,shell会将SIGINT信号传给由其启动的所有进程,以此告知出现的状况。

你可能也注意到了,shell会将这些信号传给shell脚本来处理。而shell脚本的默认行为是忽略这些信号,因为可能不利于脚本运行。要避免这种情况,可以在脚本中加入识别信号的代码,并做相应的处理。

2.产生信号

bash shell允许使用键盘上的组合键来生成两种基本的Linux信号。这个特性在需要停止或暂停失控脚本时非常方便。

1.中断进程

Ctrl+C组合键会生成SIGINT信号,并将其发送给当前在shell中运行的所有进程。通过执行一条需要很长时间才能完成的命令,然后按下Ctrl+C组合键,可以对此进行测试:

$ sleep 60
^C
$

sleep命令会按照指定的秒数暂停shell操作一段时间,然后返回shell提示符。Ctrl+C组合键会发送SIGINT信号,停止shell中当前运行的进程。在超时前(60秒)按下Ctrl+C组合键,就可以提前终止sleep命令。

2.暂停进程

你也可以暂停进程,而不是将其终止。尽管有时这可能比较危险(比如,脚本打开了一个关键的系统文件的文件锁),但它往往可以在不终止进程的情况下,使你能够深入脚本内部一窥究竟。

Ctrl+Z组合键会生成SIGTSTP信号,停止shell中运行的任何进程。停止(stopping)进程跟终止(terminating)进程不同,前者让程序继续驻留在内存中,还能从上次停止的位置继续运行。

当使用Ctrl+Z组合键时,shell会通知你进程已经被停止了:

$ sleep 60
^Z
[1]+  Stopped        sleep 60
$

方括号中的数字是shell分配的作业号。shell将运行的各个进程称为作业,并为作业在当前shell内分配了唯一的作业号。作业号从1开始,然后是2,依次递增。

如果shell会话中有一个已停止的作业,那么在退出shell时,bash会发出提醒:

$ sleep 70
^Z
[2]+  Stopped        sleep 70
$
$ exit
logout
There are stopped jobs.
$

可以用ps命令查看已停止的作业:

$ ps -l
F S    UID    PID    PPID  [...]  TTY        TIME  CMD
0 S   1001   1509    1508  [...]  pts/0  00:00:00  bash
0 T   1001   1532    1509  [...]  pts/0  00:00:00  sleep
0 T   1001   1533    1509  [...]  pts/0  00:00:00  sleep
0 R   1001   1534    1509  [...]  pts/0  00:00:00  ps
$

在S列(进程状态)中,ps命令将已停止作业的状态显示为T。这说明命令要么被跟踪,要么被停止。

如果在有已停止作业的情况下仍旧想退出shell,则只需再输入一遍exit命令即可。shell会退出,终止已停止作业。

或者,如果知道已停止作业的PID,那就可以用kill命令发送SIGKILL(9)信号将其终止:

$ kill -9 1532
[1]-  Killed        sleep 60
$ kill -9 1533
[2]+  Killed        sleep 70
$

每当shell生成命令行提示符时,也会显示shell中状态发生改变的作业。“杀死”作业后,shell会显示一条消息,表示运行中的作业已被“杀死”,然后生成提示符。

注意        在某些Linux系统中,“杀死”作业时不会得到任何回应。但当下次执行能让shell生成命令行提示符的操作时(比如,按下Enter键),你会看到一条消息,表示作业已被“杀死”。

 3.捕获信号

你也可以用其他命令在信号出现时将其捕获,而不是忽略信号。trap命令可以指定shell脚本需要侦测并拦截的Linux信号。如果脚本收到了trap命令中列出的信号,则该信号不再由shell处理,而是由本地处理。

trap命令的格式如下:

trap  commands  signals

在trap命令中,需要在commands部分列出想要shell执行的命令,在signals部分列出想要捕获的信号(多个信号之间以空格分隔)。指定信号的时候,可以使用信号的值或信号名。

下面这个简单的例子展示了如果使用trap命令捕获SIGINT信号并控制脚本的行为:

$ cat trapsignal.sh
#!/bin/bash
#Testing signal trapping
#
trap "echo ' Sorry! I have trapped Ctrl-C'" SIGINT
#
echo "This is a test script."
#
count=1
while [ $count -le 5 ]
doecho "Loop #$count"sleep 1count=$[ $count + 1 ]
done
#
echo "This is the end of test script."
exit
$

每次侦测到SIGINT信号时,本例中的trap命令都会显示一行简单的文本消息。捕获这些信号可以阻止用户通过组合键Ctrl+C停止脚本:

$ ./trapsignal.sh
This is a test script.
Loop #1
Loop #2
^C Sorry! I have trapped Ctrl-C
Loop #3
^C Sorry! I have trapped Ctrl-C
Loop #4
Loop #5
This is the end of test script.
$

每次使用Ctrl+C组合键,脚本都会执行trap命令中指定的echo语句,而不是忽略信号并让shell停止该脚本。

警告        如果脚本中的命令被信号中断,使用带有指定命令的trap未必能让被中断的命令继续执行。为了保证脚本中的关键操作不被打断,请使用带有空操作命令的trap以及要捕获的信号列表,例如:

trap "" SIGINT

这种形式的trap命令允许脚本完全忽略SIGINT信号,继续执行重要的工作。

 4.捕获脚本退出

除了在shell脚本中捕获信号,也可以在shell脚本退出时捕获信号。这是在shell完成任务时执行命令的一种简便方法。

要捕获shell脚本的退出,只需在trap命令后加上EXIT信号即可:

$ cat trapexit.sh
#!/bin/bash
#Testing exit trapping
#
trap "echo Goodbye..." EXIT
#
count=1
while [ $count -le 5 ]
doecho "Loop #$count"sleep 1count=$[ $count + 1 ]
done
#
exit
$
$ ./trapexit.sh
Loop #1
Loop #2
Loop #3
Loop #4
Loop #5
Goodbye...
$

当脚本运行到正常的退出位置时,触发了EXIT,shell执行了在trap中指定的命令。如果提前退出脚本,则依然能捕获到EXIT:

$ ./trapexit.sh
Loop #1
Loop #2
Loop #3
^CGoodbye...$

因为SIGINT信号并未在trap命令的信号列表中,所以当按下Ctrl+C组合键发送SIGINT信号时,脚本就退出了。但在退出之前已经触发了EXIT,于是shell会执行trap命令。

5.修改或移除信号捕获

要想在脚本中的不同位置进行不同的信号捕获处理,只需重新使用带有新选项的trap命令即可:

$ cat trapmod.sh
#!/bin/bash
#Modifying a set trap
#
trap "echo ' Sorry...Ctrl-C is trapped.'" SIGINT
#
count=1
while [ $count -le 3 ]
doecho "Loop #$count"sleep 1count=$[ $count + 1 ]
done
#
trap "echo ' I have modified the trap!'" SIGINT
#
count=1
while [ $count -le 3 ]
doecho "Second Loop #$count"sleep 1count=$[ $count + 1 ]
done
#
exit
$

修改了信号捕获之后,脚本处理信号的方式就会发生变化。但如果信号是在捕获被修改前接收到的,则脚本仍然会根据原先的trap命令处理该信号。

$ ./trapmod.sh
Loop #1
^C Sorry...Ctrl-C is trapped.
Loop #2
Loop #3
Second Loop #1
Second Loop #2
^C I have modified the trap!
Second Loop #3
$

提示        如果在交互式shell会话中使用trap命令,可以使用trap -p查看被捕获的信号。如果什么都没有显示,则说明shell会话按照默认方式处理信号。

也可以移除已设置好的信号捕获。在trap命令与希望恢复默认行为的信号列表之间加上两个连字符即可。 

$ cat trapremoval.sh
#!/bin/bash
#Removing a set trap
#
trap "echo ' Sorry...Ctrl-C is trapped.'" SIGINT
#
count=1
while [ $count -le 3 ]
doecho "Loop #$count"sleep 1count=$[ $count + 1 ]
done
#
trap -- SIGINT
echo "The trap is now removed."
#
count=1
while [ $count -le 3 ]
doecho "Second Loop #$count"sleep 1count=$[ $count + 1 ]
done
#
exit
$

注意        也可以在trap命令后使用单连字符来恢复信号的默认行为。单连字符和双连字符的效果一样。

移除信号捕获后,脚本会按照默认行为处理SIGINT信号,也就是终止脚本运行。但如果信号是在捕获被移除前接收到的,那么脚本就会按照原先trap命令中的设置进行处理: 

$ ./trapremoval.sh
Loop #1
Loop #2
^C Sorry...Ctrl-C is trapped.
Loop #3
The trap is now removed.
Second Loop #1
Second Loop #2
^C
$

在本例中,第一个Ctrl+C组合键用于提前终止脚本。因为信号是在捕获被移除前接收的,所以脚本会按照事先的安排,执行trap中指定的命令。捕获随后会被移除,再按Ctrl+C组合键就能提前终止脚本了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69616.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025web寒假作业二

一、整体功能概述 该代码构建了一个简单的后台管理系统界面,主要包含左侧导航栏和右侧内容区域。左侧导航栏有 logo、管理员头像、导航菜单和安全退出按钮;右侧内容区域包括页头、用户信息管理内容(含搜索框和用户数据表格)以及页…

Node.js怎么调用到打包的python文件呢

在 Node.js 中调用打包后的 Python 可执行文件(如 PyInstaller 生成的 .exe 或二进制文件),可以通过以下步骤实现: 一、Python 打包准备 假设已有打包好的 Python 文件 your_script.exe(以 Windows 为例)&…

【开源AI】AI一页一页读PDF

【开源AI】AI一页一页读PDF 可以在这里看 : 让AI 处理 PDF 文件,提取其中的知识点,并生成总结。 只是无法修改,后续若有更新在csdn这里。 【OpenAI】 API 更新: JSON 结构化输出约束机制( JSON Schema) 的一次实战。知识库的JSON Schema形式 每一页都要总结,总结的知识…

如何在 Qt 中添加和使用系统托盘图标

在 Qt 中实现系统托盘图标是一个常见的需求,尤其是在桌面应用程序中。系统托盘图标可以让应用程序在后台运行时仍然具有可见性,同时避免占用过多的桌面空间。本文将详细介绍如何在 Qt 项目中添加托盘图标,并通过资源系统(.qrc 文件…

探索B-树系列

🌈前言🌈 本文将讲解B树系列,包含 B-树,B树,B*树,其中主要讲解B树底层原理,为什么用B树作为外查询的数据结构,以及B-树插入操作并用代码实现;介绍B树、B*树。 &#x1f4…

Python的

& 运算符可用于不同集合类型,它主要用于集合的交集操作 下面分别介绍它在 set(集合)和 frozenset(不可变集合)这两种常见集合类型中的使用 set 类型 set 是 Python 中内置的可变集合类型,使用 & …

深入与浅出-Python爬虫逆向实战

一、什么是爬虫逆向? 爬虫逆向,简单来说,就是通过分析网页的前端和后端行为,找出数据的来源和获取方式,从而实现自动化抓取。很多时候,直接使用requests和BeautifulSoup可能无法获取到目标数据&#xff0c…

使用 POI-TL 和 JFreeChart 动态生成 Word 报告

文章目录 前言一、需求背景二、方案分析三、 POI-TL JFreeChart 实现3.1 Maven 依赖3.3 word模板设置3.2 实现代码 踩坑 前言 在开发过程中,我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart,实现动态生成 W…

Java网络编程学习(一)

网络相关概念 网络体系结构 OSI体系结构(七层) OSI(Open Systems Interconnection,开放系统互联)体系结构将整个计算机网络分为七层,从上到下依次为:应用层、表示层、会话层、传输层、网络层…

flutter ListView Item复用源码解析

Flutter 的 ListView 的 Item 复用机制是其高性能列表渲染的核心,底层实现依赖于 Flutter 的渲染管线、Element 树和 Widget 树的协调机制。以下是 ListView 复用机制的源码级解析,结合关键类和核心逻辑进行分析。 1. ListView 的底层结构 ListView 的复…

粒子群优化算法:像鸟群一样找到最优解

前言 在人工智能的浩瀚星空中,粒子群优化算法(PSO)如同一颗熠熠生辉的明星,吸引了无数科研人员的目光。它的名字听起来好像非常高大上,仿佛只有数学天才和算法大师才能理解。但实际上,PSO的原理并没有那么复杂。想象一下,一群聪明的小鸟在天空中自由飞翔,大家互相呼唤…

QT修仙之路2-2 对话框 尚欠火候

警告对话框 相关代码 错误对话框 相关代码 消息对话框 相关代码 询问对话框 相关代码 相关代码 警告对话框 QMessageBox::warning(this,"错误","账号密码不能为空",QMessageBox::Ok);错误对话框 QMessageBox msgBox(QMessageBox::Critical,"错误…

Python 字典(一个简单的字典)

在本章中,你将学习能够将相关信息关联起来的Python字典。你将学习如何访问和修改字典中的信息。鉴于字典可存储的信息量几乎不受限制,因此我们会演示如何遍 历字典中的数据。另外,你还将学习存储字典的列表、存储列表的字典和存储字典的字典。…

conda 修复 libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found 简便方法

ImportError: /data/home/hum/anaconda3/envs/ipc/bin/../lib/libstdc.so.6: version GLIBCXX_3.4.30 not found (required by /home/hum/anaconda3/envs/ipc/lib/python3.11/site-packages/paddle/base/libpaddle.so) 1. 检查版本 strings /data/home/hum/anaconda3/envs/ipc/…

RTD2775QT/RTD2795QT瑞昱显示器芯片方案

RTD2775QT与RTD2795QT:高性能4K显示驱动芯片 RTD2775QT与RTD2795QT是瑞昱半导体公司推出的两款高性能显示驱动芯片,专为满足现代显示设备对高清、高分辨率的需求而设计。这两款芯片不仅支持4K分辨率,还具备丰富的功能和卓越的性能&#xff0…

Linux路径中的‘~‘

本文来自DeepSeek 在Linux中,~ 是用户主目录的简写。具体含义如下: 当前用户的主目录: ~ 代表当前登录用户的主目录。例如,用户 alice 的主目录通常是 /home/alice,~ 就指向 /home/alice。 其他用户的主目录&#xff…

【含开题报告+文档+PPT+源码】学术研究合作与科研项目管理应用的J2EE实施

开题报告 本研究构建了一套集注册登录、信息获取与科研项目管理于一体的综合型学术研究合作平台。系统用户通过注册登录后,能够便捷地接收到最新的系统公告和科研动态新闻,并能进一步点击查看详尽的新闻内容。在科研项目管理方面,系统提供强…

ES6 Proxy 用法总结以及 Object.defineProperty用法区别

Proxy 是 ES6 引入的一种强大的拦截机制,用于定义对象的基本操作(如读取、赋值、删除等)的自定义行为。相较于 Object.defineProperty,Proxy 提供了更灵活、全面的拦截能力。 1. Proxy 语法 const proxy new Proxy(target, hand…

力扣 单词拆分

动态规划,字符串截取,可重复用,集合类。 题目 单词可以重复使用,一个单词可用多次,应该是比较灵活的组合形式了,可以想到用dp,遍历完单词后的状态的返回值。而这里的wordDict给出的是list&…

Node.js 环境配置

什么是 Node.js Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行时环境,它允许你在服务器端运行 JavaScript。传统上,JavaScript 主要用于浏览器中的前端开发,而 Node.js 使得 JavaScript 也能够在服务器上执行,…