上一篇讲了分享了shell中的逻辑控制,如未阅读可翻看老王linux-进阶-shell编程(二)逻辑控制判断、循环、函数,本篇我们分享下信号捕获+异步控制
日常在脚本执行过程中,不小心按了Ctrl+C,脚本直接终止,留下一堆临时文件、未完成的任务,想让脚本同时执行多个任务(比如一边备份日志、一边监控磁盘),但脚本只能顺序执行,效率极低。这些问题都能通过「Shell信号捕获(trap)」和「异步执行」轻松解决。
我们在操作Linux终端时,其实一直在和“信号”打交道。
简单说,信号就是系统给进程(脚本、命令)发送的“指令”,比如“终止进程”“暂停进程”“重新加载配置”,进程收到信号后,会执行对应的操作。
对于Shell脚本来说,最常用、最需要关注的3个信号
•SIGINT(2):用户按下Ctrl+C发送的信号,作用是“强制终止进程”(最常用,也是最容易导致脚本失控的信号);
•SIGTERM(15):系统或用户发送的“正常终止信号”,进程收到后会先清理资源,再终止(推荐用这个信号终止脚本);
•SIGQUIT(3):用户按下Ctrl+\发送的信号,作用是“终止进程并生成核心转储文件”(用于调试,日常用得少)。
可以用kill -l命令,查看Linux系统所有信号(共64个)。
一、trap命令——捕获信号
trap命令的核心作用:捕获系统发送给脚本的信号,然后执行我们预设的操作,而不是让脚本“被动终止”。
比如:捕获Ctrl+C(SIGINT)信号后,不直接终止脚本,而是先清理临时文件、输出提示信息,再优雅退出——这就是trap的价值。
1. trap基本语法
bash# 语法:trap "捕获信号后执行的命令" 信号1 信号2 ...trap "echo '脚本被中断,正在清理资源...'; rm -f /tmp/temp.txt" SIGINT SIGTERM
说明:
•双引号内:是捕获到信号后,要执行的操作(可以是多个命令,用分号分隔);
•后面的SIGINT、SIGTERM:是要捕获的信号(可以写信号名,也可以写信号编号,比如2代表SIGINT);
•作用:当脚本收到SIGINT(Ctrl+C)或SIGTERM(正常终止)信号时,先输出提示,再删除临时文件,避免资源残留。
2. :用trap实现脚本“优雅退出”
写一个日志备份脚本,若脚本被Ctrl+C中断,要先清理临时备份文件,再提示用户,避免残留文件占用磁盘。
bash#!/bin/bash# 脚本名称:log_backup.sh# 功能:日志备份,捕获信号实现优雅退出# 作者:XXX# 定义临时文件和备份目录(根据实际环境修改)TEMP_FILE="/tmp/log_temp.tar.gz"BACKUP_DIR="/data/backup"# 1. 定义信号捕获函数(复杂操作建议封装成函数,更清晰)cleanup() {echo -e "\n⚠️ 脚本被中断(Ctrl+C或终止信号)"echo "正在清理临时文件..."# 清理临时文件if [ -f $TEMP_FILE ]; thenrm -f $TEMP_FILEecho "✅ 临时文件清理完成"fiecho "脚本优雅退出~"exit 0 # 正常退出脚本}# 2. 捕获信号:SIGINT(Ctrl+C)、SIGTERM(正常终止)trap cleanup SIGINT SIGTERM# 3. 脚本核心逻辑(日志备份)echo "✅ 开始日志备份..."# 先创建临时备份文件tar -zcvf $TEMP_FILE /var/log/*.log 2>/dev/null# 备份完成后,移动到备份目录mv $TEMP_FILE $BACKUP_DIR/$(date +%Y%m%d)_log.tar.gzecho "✅ 日志备份完成,备份文件:$BACKUP_DIR/$(date +%Y%m%d)_log.tar.gz"exit 0
测试效果:
运行脚本后,按下Ctrl+C,脚本不会直接终止,而是执行cleanup函数:清理临时文件→输出提示→优雅退出。
3. trap常见用法
1.场景1:清理资源(最常用):捕获终止信号,清理临时文件、释放资源(如关闭进程、删除锁文件);
2.场景2:忽略信号:不想让脚本被某个信号终止(比如禁止Ctrl+C中断),写法:trap "" SIGINT(双引号内为空,即捕获后不执行任何操作);
3.场景3:恢复默认行为:取消之前的trap设置,恢复信号的默认行为,写法:trap - SIGINT SIGTERM(减号表示取消)。
trap使用的3个注意点
•trap命令要写在脚本开头,先设置捕获,再执行核心逻辑(否则信号可能捕获不到);
•捕获信号后,执行的操作要简洁,避免在cleanup函数中写复杂逻辑(比如长时间备份);
•不要忽略SIGTERM信号(正常终止信号),很多运维工具会用这个信号终止脚本,忽略后可能导致脚本无法正常停止。
二、核心技巧2:Shell异步执行——让脚本“多任务并行”
默认情况下,Shell脚本是“顺序执行”的——执行完一个命令,再执行下一个,效率很低。比如:备份日志需要10分钟,监控磁盘需要5分钟,顺序执行就要15分钟;但如果让它们“同时执行”(异步),5分钟就能完成,效率直接翻倍
Shell异步执行的核心:在命令后面加 & 符号,让命令在后台运行,脚本继续执行下一个命令。
1. 异步执行基本用法
bash# 语法:命令 & (& 放在命令末尾,让命令后台异步运行)# 案例:后台备份日志,同时执行磁盘监控,互不影响echo "开始后台备份日志..."tar -zcvf /data/backup/log.tar.gz /var/log/*.log &echo "开始监控磁盘使用率..."df -h | grep /dev/vda1
效果:备份日志的命令在后台运行,脚本不会等待它执行完成,直接执行后面的磁盘监控命令,两个任务并行进行。
2. 实战案例:异步执行+信号捕获,实现多任务稳健运行
场景:同时执行两个任务——后台备份日志、后台监控磁盘,若脚本被中断,要同时清理两个任务的临时资源,优雅退出。
bash#!/bin/bash# 脚本名称:async_task.sh# 功能:异步执行多任务,结合trap实现优雅退出# 作者:XXX# 定义临时文件和后台进程ID(用于后续终止后台进程)LOG_BACKUP_TEMP="/tmp/log_backup_temp.tar.gz"DISK_MONITOR_TEMP="/tmp/disk_monitor.log"# 用于存储后台进程IDBACKUP_PID=""MONITOR_PID=""# 1. 定义清理函数(终止后台进程+清理临时文件)cleanup() {echo -e "\n⚠️ 脚本被中断,正在终止后台进程、清理资源..."# 终止后台备份进程(如果存在)if [ -n "$BACKUP_PID" ]; thenkill -SIGTERM $BACKUP_PID 2>/dev/nullecho "✅ 后台备份进程已终止"fi# 终止后台监控进程(如果存在)if [ -n "$MONITOR_PID" ]; thenkill -SIGTERM $MONITOR_PID 2>/dev/nullecho "✅ 后台监控进程已终止"fi# 清理临时文件rm -f $LOG_BACKUP_TEMP $DISK_MONITOR_TEMPecho "✅ 临时文件清理完成,脚本优雅退出~"exit 0}# 2. 捕获信号,执行清理操作trap cleanup SIGINT SIGTERM# 3. 异步执行任务1:后台备份日志echo "✅ 任务1:后台备份日志(进程将在后台运行)"tar -zcvf $LOG_BACKUP_TEMP /var/log/*.log >/dev/null 2>&1 &# 保存后台进程ID($! 表示上一个后台进程的PID)BACKUP_PID=$!# 4. 异步执行任务2:后台监控磁盘(每5秒监控一次)echo "✅ 任务2:后台监控磁盘(每5秒输出一次使用率)"while true; dodf -h | grep /dev/vda1 >> $DISK_MONITOR_TEMPsleep 5done &# 保存后台进程IDMONITOR_PID=$!# 5. 等待所有后台进程完成(避免脚本提前退出)wait $BACKUP_PIDwait $MONITOR_PID# 6. 所有任务完成后,清理临时文件rm -f $LOG_BACKUP_TEMP $DISK_MONITOR_TEMPecho "✅ 所有异步任务执行完成!"exit 0
说明:
•$!:特殊变量,获取上一个后台进程的PID(进程ID),用于后续终止后台进程;
•wait:等待指定后台进程执行完成,避免脚本提前退出(如果不写wait,脚本会直接执行完最后一行,退出后后台进程也会被终止);
•异步任务的临时文件、进程ID,一定要在cleanup函数中清理,避免脚本中断后残留。
高频场景
•多任务并行:同时执行备份、监控、日志清理等任务,提升脚本效率;
•长时间任务后台运行:将耗时任务(如大文件备份)放在后台,脚本继续执行其他操作;
•定时任务联动:让多个定时任务异步执行,互不干扰。
后续不管是写日常运维脚本,还是复杂的自动化脚本,这两个技巧都能帮你避开很多坑,让你的脚本更专业、更稳健~
如果觉得本文有用,记得点赞、在看、转发,关注我,每天学一点Shell小技巧,从新手进阶到运维高手。