使用管道命令忽略Bash脚本中的HUP信号

我有以下脚本无限期监视/ tmp目录,如果该目录中有文件操作,则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

这是实际脚本的简化版本.我上面的脚本也有一个Sys-V类型的init脚本,因为我希望保持LSB兼容,我的init脚本有force-reload(如果服务支持,则重新加载配置.否则,服务重新启动) .)将HUP信号发送到脚本的选项.现在在执行执行killproc -HUP test.sh的force-reload之前,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信号?

最佳答案
管道命令在子shell中运行

问题的第一部分是通过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和以下链接:

> Anonymous pipe, Wikipedia article
> Unix Pipeline, Wikipedia article

信号处理

我花了很长时间(实际上是几个小时的研究)来弄清楚为什么SIGHUP信号的陷阱没有被忽略.

我的所有研究表明,由clone(2)系统调用创建的子进程也应该能够共享父进程的信号处理程序表.

Bash手册页也说明了这一点

Command substitution, commands grouped with parentheses, and asynchronous commands are invoked in a subshell environment that is a duplicate of the shell environment, except that traps caught by the shell are reset to the values that the shell inherited from its parent at invocation.

它后来说明了这一点

Signals ignored upon entry to the shell cannot be trapped or reset. Trapped signals that are not being ignored are reset to their original values in a subshell or subshell environment when one is created.

这表明子shell不继承未被忽略的信号处理程序.正如我所理解的那样,你的陷阱’:’HUP行意味着(有效)忽略了SIGHUP信号(因为:builtin除了返回成功之外什么都不做) – 而且应该被管道的子shell忽略.

但是,我最终遇到了Bash手册页中内置陷阱的描述,该手册定义了忽略Bash的含义:

If arg is the null string the signal specified by each sigspec is ignored by the shell and by the commands it invokes.

简单地将陷阱命令更改为陷阱”HUP可确保忽略SIGHUP信号,脚本本身以及任何子壳.

转载注明原文:使用管道命令忽略Bash脚本中的HUP信号 - 代码日志