简介
脚本运行时经常会创建临时文件或启动后台进程。如果用户中断脚本(例如按下 Ctrl+C),这些临时文件通常会残留,造成系统杂乱。
为避免这种情况,脚本需要能够检测自身何时即将停止,并执行必要的清理操作。
在 Linux 中,我们使用 trap 命令捕获中断信号和退出事件。信号是操作系统与进程通信的方式之一。例如,按下 Ctrl+C 通常会发送 SIGINT,请求进程停止。
这样,我们就可以定义一个「退出时」执行的过程,让脚本在终止前完成自身清理。
在本实验中,你将:
- 创建一个脚本,生成临时文件。
- 中断脚本,查看残留文件。
- 实现 trap,在脚本退出时自动删除临时文件。
创建长时间运行的进程
首先,创建一个模拟长时间运行任务的脚本。该脚本会创建一个临时文件,然后无限等待。
长时间运行的脚本在系统管理工作中很常见。它们可能会监视日志、等待输入、轮询服务,或持续处理任务。脚本运行的时间越长,被中断时正确清理资源就越重要。
在项目目录中创建并打开 long_process.sh:
cd ~/project
nano long_process.sh
输入以下代码:
#!/bin/bash
## Create a temporary file
touch temp_data.txt
echo "Temporary file 'temp_data.txt' created."
## Fall asleep (Simulate a long job)
echo "Process is running. Press Ctrl+C to stop..."
while true; do
sleep 1
done
保存(Ctrl+O,然后按 Enter)并退出(Ctrl+X)。
为脚本添加可执行权限:
chmod +x long_process.sh
问题:如果运行此脚本,然后使用 Ctrl+C 停止它,文件 temp_data.txt 仍会保留在磁盘上。这是不好的实践,因为反复运行脚本可能会产生大量无用文件,最终占满磁盘空间。
临时文件只是一个典型例子。同样的问题也可能出现在锁文件、套接字、挂载点或后台辅助进程上。清理的核心目标,是让系统始终保持在可预期的状态。
定义清理函数
为了正确处理清理操作,最佳实践是定义一个专用函数,将所有清理逻辑集中在其中。
将清理逻辑放在单独的函数中,可以使脚本结构更清晰。这样,不同的退出路径都可以复用同一套清理逻辑,而不必在多个位置重复编写。
再次打开脚本:
nano long_process.sh
在脚本顶部添加 cleanup 函数:
#!/bin/bash
## Define the cleanup function
cleanup() {
echo ""
echo "Cleaning up temporary files..."
rm -f temp_data.txt
echo "Cleanup complete."
}
## Create a temporary file
touch temp_data.txt
echo "Temporary file 'temp_data.txt' created."
## Simulate a long-running process
echo "Process is running. Press Ctrl+C to stop..."
while true; do
sleep 1
done
保存并退出。
此时,我们已经定义了函数,但还没有告诉脚本何时调用它。
设置 Trap 捕获信号
现在使用 trap 命令告诉 Bash:「如果收到中断信号(例如 Ctrl+C),就运行 cleanup 函数。」
这是 Shell 脚本响应操作系统事件的典型示例,而不仅仅是处理文本输入。信号是系统或用户异步发送给进程的消息,而 trap 让脚本有机会主动做出响应。
打开脚本:
nano long_process.sh
将 trap 命令添加到函数定义下方、主要逻辑开始之前。
#!/bin/bash
cleanup() {
echo ""
echo "Cleaning up temporary files..."
rm -f temp_data.txt
echo "Cleanup complete."
exit
}
## Register the trap
## SIGINT = Ctrl+C
## EXIT = Any exit (success or failure)
trap cleanup SIGINT EXIT
## The rest of the script...
touch temp_data.txt
echo "Temporary file 'temp_data.txt' created."
echo "Process is running. Press Ctrl+C to stop..."
while true; do
sleep 1
done
**理解 trap arguments**:
cleanup:要运行的命令或函数。SIGINT:按下Ctrl+C时发送的信号。EXIT:一种伪信号,脚本因任何原因退出时都会触发。
同时使用 SIGINT 和 EXIT 可以让清理操作更加可靠。SIGINT 负责处理常见的键盘中断,EXIT 则为正常终止和许多失败情况提供更通用的保障。
保存并退出。
验证清理操作
现在验证清理机制是否正常工作。
运行脚本:
./long_process.sh
脚本会创建 temp_data.txt,然后开始运行。
现在按下 Ctrl+C 中断脚本。
你应该会看到以下输出:
^C
Cleaning up temporary files...
Cleanup complete.
最后,检查文件是否已删除:
ls temp_data.txt
命令应该返回类似以下错误:
ls: cannot access 'temp_data.txt': No such file or directory
这说明 trap 命令成功拦截了停止信号,并在脚本终止前执行了清理代码。
这是专业脚本编写中的一个良好习惯:如果脚本创建了临时状态,就应该同时定义如何删除这些状态。trap 是 Bash 为此提供的主要工具之一。
总结
在本实验中,你学习了如何使用 Trap 编写更健壮的脚本。
- 信号:理解
Ctrl+C会向进程发送SIGINT信号。 - Trap:使用
trap捕获信号并执行指定代码。 - 清理:自动执行维护任务,保持系统整洁。
专业脚本总会处理各种退出场景,以避免数据损坏和文件残留。



