如果我运行如下命令:
cat <(echo 1 | pv) | pv
cat <(echo 1 | pv) | less
cat <(echo 1 | pv) | cat
该命令似乎会永远运行。输入^C
(SIGINT) 会杀死整个 shell,而不仅仅是执行的命令。为什么会这样呢?
ps xf
最小情况下另一个 shell 的相关输出cat <(pv) | less
:
Ss /bin/bash
S+ \_ cat /dev/fd/XX
S | \_ /bin/bash
T | \_ pv
S+ \_ less
打开这些文件描述符:
巴什
0 -> /dev/pts/YY
1 -> /dev/pts/YY
2 -> /dev/pts/YY
255 -> /dev/pts/YY
猫 /dev/fd/ZZ
0 -> /dev/pts/YY
1 -> pipe:[RRRRRRRR]
2 -> /dev/pts/YY
3 -> pipe:[QQQQQQQQ]
ZZ -> pipe:[QQQQQQQQ]
巴什
0 -> /dev/pts/YY
1 -> pipe:[QQQQQQQQ]
2 -> /dev/pts/YY
255 -> /dev/pts/YY
光伏发电
0 -> /dev/pts/YY
1 -> pipe:[QQQQQQQQ]
2 -> /dev/pts/YY
较少的
0 -> pipe:[RRRRRRRR]
1 -> /dev/pts/YY
2 -> /dev/pts/YY
3 -> /dev/tty
使用原始示例(当不是 bash 内置程序而是另一个程序cat <(echo 1 | pv) | less
时,也会发生这种情况,例如:echo
dd if=/dev/zero bs=1 count=1
Ss /bin/bash
S+ \_ cat /dev/fd/63
S | \_ /bin/bash
T | \_ pv
S+ \_ less
巴什
0 -> /dev/pts/18
1 -> /dev/pts/18
2 -> /dev/pts/18
255 -> /dev/pts/18
猫 /dev/fd/63
0 -> /dev/pts/18
1 -> pipe:[36932796]
2 -> /dev/pts/18
3 -> pipe:[36929317]
63 -> pipe:[36929317]
巴什
0 -> /dev/pts/18
1 -> pipe:[36929317]
2 -> /dev/pts/18
255 -> /dev/pts/18
光伏发电
0 -> pipe:[36930391]
1 -> pipe:[36929317]
2 -> /dev/pts/18
较少的
0 -> pipe:[36932796]
1 -> /dev/pts/18
2 -> /dev/pts/18
3 -> /dev/tty
答案1
发生这种情况是因为该<(
过程)
没有得到适当的作业控制——它只是被分叉并忘记了。大多数时候这并不重要,因为几乎该进程一诞生就被放置在一个单独的进程组中并处于后台。为了立即的shell 需要打开该进程的输入和输出,但是,这是 tty 的前台进程组,因此容易受到 SIGINT 的影响 - 除非它像交互式 shell 通常那样被捕获或忽略。
但问题是:管道出现了僵局。当父进程尝试打开该进程的输出时,管道会阻塞。它永远没有机会改变进程组和所有其他的,因为当你CTRL+C
前台组被杀死 - 它被发送 SIGINT - 当前台组死亡并且父组无法恢复控制时,因为它仍然被管道阻塞,终端会发送一个 HUP,因为家里没人。卡布姆
您首先需要一个编写器,然后为您打开的每个管道提供一个读取器,然后才能打开另一个管道。