如何在 2 个子进程之间创建匿名管道并知道它们的 pids(而不使用 files/named 管道)?

How to create an anonymous pipe between 2 child processes and know their pids (while not using files/named pipes)?

请注意,这个问题是在我收到一些评论后编辑的。最初我想将我的目标分成更小的部分以使其更简单(并且可能 ps 扩展我在各个方面的知识),但似乎我过于简单了:)。所以,我在这里问一个大问题。

使用 bash,有没有一种方法可以在两个子进程之间实际创建一个匿名管道并知道它们的 pids?

我问的原因是当您使用经典管道时,例如

cmd1 | cmd2 &

您无法向 cmd1 发送信号。在我的例子中,我 运行 的实际命令是这些

./my_web_server | ./my_log_parser &

my_web_server 是一个基本的网络服务器,可以将大量日志记录信息转储到它的标准输出 my_log_parser 是我写的一个日志解析器,它读取从 my_web_server 接收到的所有日志信息,它基本上只从日志中选择某些值(实际上它实际上存储了它接收到的整个日志,但另外它会创建一个额外的 csv 文件,其中包含它找到的值)。

我遇到的问题是 my_web_server 实际上从来不会单独停止 ps(它是一个网络服务器,您不希望从网络服务器获得它:))。所以在我完成之后,我需要自己停止它。我希望 bash 脚本在我通过 SIGINT 或 SIGTERM 停止时执行此操作(bash 脚本)。

对于这样的事情,traps 是必经之路。本质上,我会为 INT 和 TERM 创建一个陷阱,它调用的函数会杀死 my_web_server,但是......我没有 pid,即使我知道我可以通过 [=51 查找它=],我正在寻找一个漂亮的解决方案 :)。

有些人可能会说:"Well, why don't you just kill my_log_parser and let my_web_server die on its own with SIGPIPE?"。我不想杀死它的原因是当你杀死一个位于管道末端的进程时,它之前的进程的输出缓冲区不会被刷新。所以,你丢东西了。

我在这里和其他地方看到了几个建议将 my_web_server 的 pid 存储在文件中的解决方案。这是一个有效的解决方案。可以通过稍微摆弄文件描述符来编写管道。但是,我不喜欢这个解决方案,因为我必须生成文件。我不喜欢创建任意文件只是为了存储 5 个字符的 PID :).

我现在最后做的是:

#!/bin/bash

trap " " HUP

fifo="$( mktemp -u "$( basename "[=13=]" ).XXXXXX" )"
mkfifo "${fifo}"

<"${fifo}" ./my_log_parser &
parser_pid="$!"

>"${fifo}" ./my_web_server &
server_pid="$!"

rm "${fifo}"

trap '2>/dev/null kill -TERM '"${server_pid}"'' INT TERM

while true; do
  wait "${parser_pid}" && break
done

这解决了当脚本接收到 SIGINT 或 SIGTERM 时无法终止 my_web_server 的问题。它似乎比任何摆弄文件描述符以最终使用文件存储 my_web_server 的 pid 的黑客更具可读性,我认为这很好,因为它提高了可读性。

但它仍然使用文件(命名管道)。尽管我知道它使用 my_web_server 和 my_log_parser 的文件(命名管道)进行通信(这是一个很好的理由)并且文件在创建后很快就会从磁盘上擦除,但它仍然一个文件 :).

你们中有人知道不使用任何文件(命名管道)来完成这项任务的方法吗?

来自 Bash man 页:

!      Expands  to  the  process ID of the most recently executed back-
       ground (asynchronous) command.

你不是 运行 后台命令,你是 运行 进程替换以读取文件描述符 3。

以下有效,但我不确定这是否是您要实现的目标:

sleep 120 &
child_pid="$!"

wait "${child_pid}"
sleep 120

编辑: 评论是:我知道我几乎可以用愚蠢的 'while read i; do blah blah; done < <( ./my_proxy_server )' 方式来做到这一点,但我并不特别喜欢这样一个事实,即当使用这种方法的脚本接收到 INT 或 TERM 时,它就会死掉也没有告诉 ./my_proxy_server 去开玩笑 :)

所以,您的问题似乎源于获取代理服务器的PID 不是那么容易的事实。那么,如何使用您自己的命名管道,使用 trap 命令:

pipe='/tmp/mypipe'
mkfifo "$pipe"
./my_proxy_server > "$pipe" &

child_pid="$!"
echo "child pid is $child_pid"

# Tell the proxy server to bugger-off
trap 'kill $child_pid' INT TERM

while read
do
    echo $REPLY
    # blah blah blah
done < "$pipe"

rm "$pipe"

您也可以使用 kill %1 而不是 $child_pid

YAE(又一次编辑):
您问如何从以下位置获取 PIDS:

./my_web_server | ./my_log_parser &

有点简单。为了测试我使用了 sleep,就像你原来的一样。

sleep 400 | sleep 500 &
jobs -l

给出:

[1]+  8419 Running                 sleep 400
      8420 Running                 | sleep 500 &

所以这只是提取那些 PIDS 的问题:

pid1=$(jobs -l|awk 'NR==1{print }')
pid2=$(jobs -l|awk 'NR==2{print }')

我讨厌在这里两次调用 awk,但其他任何事情都只是在跳过篮球。