我有以下脚本无限期地监视a
目录,如果该目录中有文件操作,则文件名由while循环读取,文件名中的第一个b
字符为替换为test.log
字符,此修改后的文件名将记录到#!/bin/bash
trap ':' HUP
trap 'kill $(jobs -p)' EXIT
/usr/local/bin/inotifywait -q -m /tmp --format %f |
while IFS= read -r filename; do
echo "$filename" | sed 's/a/b/' > test.log
done
文件:
force-reload
这是实际脚本的简化版本。我还为上面的脚本提供了一个Sys-V类型的init脚本,因为我希望保持LSB兼容,我的init脚本有force-reload
(如果服务支持,则会重新加载配置。否则,服务重新启动。)选项,将HUP信号发送到脚本。现在,在执行执行killproc -HUP test.sh
的{{1}}之前,pstree
的输出如下:
# pstree -Ap 4424
test.sh(4424)-+-inotifywait(4425)
`-test.sh(4426)
#
执行strace killproc -HUP test.sh
后,子shell终止:
# pstree -Ap 4424
test.sh(4424)---inotifywait(4425)
#
根据strace
,killproc
已将SIGHUP
发送到流程4424
和4426
,但只有后者才被终止。
在我的例子中,这个具有PID 4426
的子shell有什么意义,即为什么它首先被创建?另外,有没有办法忽略HUP
信号?
答案 0 :(得分:8)
问题的第一部分由shell(在本例中为Bash)在管道中运行命令的机制解释。
管道是一个FIFO(先进先出)单向进程间通信(IPC)通道:它允许在一端写入字节(只写端)并从另一端(只读端)读取,无需读取或写入物理文件系统。
管道允许两个不同的命令通过匿名或未命名相互通信(即,文件系统中没有条目)管。
当shell执行简单命令时,该命令在shell的子进程中运行。如果没有使用作业控制,则当子进程终止时,shell将重新获得对终端的控制。
当在管道中运行两个命令时,管道中的两个命令都作为两个独立运行的子进程执行。
在Unix系统中,使用pipe(2)
系统调用创建管道,该系统调用创建一个新管道并返回一对文件描述符,其中一个引用读取结束而另一个引用写管道的一端。
在GNU / Linux系统上使用Bash时,clone(2)
系统调用用于创建子进程。这允许子进程与其父进程共享文件描述符表,以便两个子子进程都继承匿名管道的文件描述符,以便可以读取它并且另一个可以写入它。
在您的情况下,inotifywait
命令获得4425的PID,并通过将其stdout
连接到写端的文件描述符来写入管道的只写端。
同时,管道命令的右侧获取PID,4426并将其stdin
文件描述符设置为管道的只读端的描述符。由于管道右侧的子shell不是外部命令,因此表示子进程的名称与其父进程test.sh
的名称相同。
有关详细信息,请参阅man 7 pipe
和以下链接:
我花了很长时间(实际上是几个小时的研究)来弄清楚为什么SIGHUP信号的陷阱没有被忽略。
我的所有研究表明,clone(2)
系统调用创建的子进程也应该能够共享父进程的信号处理程序表。
Bash手册页还说明了
命令替换,用括号分组的命令和异步命令在shell shell环境中调用,该shell环境与shell环境重复,除了shell捕获的陷阱被重置为shell在调用时从其父级继承的值。
后来说明了
进入shell时忽略的信号无法被捕获或重置。当创建一个被捕获的信号时,未被忽略的信号将在子shell或子shell环境中重置为其原始值。
这表示子shell不继承 not ignored 的信号处理程序。据我所知,你的trap ':' HUP
行意味着(有效)忽略了SIGHUP信号(因为:
内置函数除了返回成功之外什么都不做) - 而且应该被管道的子shell忽略。
但是,我最终在Bash手册页中找到了trap
内置的描述,该页面定义了Bash的含义忽略:
如果arg是空字符串,则shell会忽略每个sigspec指定的信号及其调用的命令。
只需将trap
命令更改为trap '' HUP
即可确保忽略SIGHUP信号,以及脚本本身 - 以及任何子壳。