背景问题描述
我有一个流水线,其中长时间运行的命令输入到一个过滤器中。如果过滤器发现了"foo",我希望长时间运行的命令停止执行。
一般情况下,这是不可能的,因为兄弟进程(同一个父进程的两个子进程)之间没有彼此的信息。但考虑以下示例和答案:
我们假设以下使用Linux的inotifywait程序的流水线:
inotifywait -m --format '%e %f' uploads |
while read -r events file; do
if [[ $events = *MOVED_TO* ]]; then
: do something special
if [[ $file = *abort* ]]; then
: special sentinel file found
: want to kill the inotifywait process
fi
fi
done
在这个示例中,用户希望在执行重命名操作时,如果结果文件名包含字符串"abort",停止inotifywait程序。我们在这里有哪些选择呢?
- 简单地从while循环中退出,并且不对inotifywait做任何显式操作。包含while循环的shell进程将终止,而inotifywait进程将继续运行。当inotifywait尝试向管道写入另一行输出时(已关闭),它将接收到一个SIGPIPE信号,从而终止它。
- 在原始生成数据的进程中查找退出的选项,当发生异常情况时,而不是依赖外部过滤器来完成这个任务。(这只适用于某些生成数据的程序,但值得花时间阅读文档并确定是否可行。)
- 使用FIFO替换匿名流水线,并将原始生成数据的进程作为后台进程显式运行。保存其PID。等待过滤器退出,一旦过滤器退出,就杀死长时间运行的生成数据进程。实现如下:
filter() {
while read -r events file; do
: ...
done
}
fifo="${TMPDIR:-${XDG_RUNTIME_DIR:-/tmp}}//fifo.$$"
trap 'rm -rf -- "$fifo"' EXIT
mkfifo -- "$fifo" || exit
inotifywait -m --format '%e %f' uploads > "$fifo" &
pid=$!
filter < "$fifo"
kill -- "$pid"
wait
知识点
inotifywait 是 Linux 内核 Inotify 事件监测机制的 Shell 前端,用于监视文件系统事件。
它主要用于:
- 监测文件和目录的修改:
- 文件是否被创建、修改、删除
- 目录是否有文件被创建、修改、删除
- 自动执行脚本:
每当监听的文件或目录有变化时,可以自动运行指定的 Shell 脚本。
如你提供的例子:
inotifywait -m --format '%e %f' uploads > "$fifo" &
它监听 uploads 目录,如果 uploads 目录下有文件被创建(create)或修改(modify),就会输出事件和文件名到"$fifo" 文件中。
这样就可以基于 inotifywait 来建立自动执行脚本的功能。一般使用它的场景有:
- 自动编译程序:监视源码变化自动重新编译
- 自动部署程序:监视代码提交自动部署最新版本
- 自动处理文件:监视文件变化自动运行处理脚本
inotifywait 可以让 Shell 脚本具备“事件驱动”的能力,比 periodically cron 更高效。