进程替换和 cat:无法读取标准输入

Suz*_*Soy 4 io-redirection process-substitution

进程替换<(cat)不起作用。为什么?

head <(cat) 
cat: -: Input/output error
Run Code Online (Sandbox Code Playgroud)

我可以使用额外的文件描述符,但我不明白为什么上述方法不起作用。

# Using file descriptor 3 to reroute the input
(head <(cat <&3)) 3<&0
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 6

bash,如果你这样做:

$ cat <(ps -j)
  PID  PGID   SID TTY          TIME CMD
 3887 16480 16480 pts/29   00:00:00 bash
 3888  3888 16480 pts/29   00:00:00 cat
 3889 16480 16480 pts/29   00:00:00 ps
16480 16480 16480 pts/29   00:00:00 bash
Run Code Online (Sandbox Code Playgroud)

zsh

$ cat <(ps -j)
  PID  PGID   SID TTY          TIME CMD
 3935  3935 16480 pts/29   00:00:00 ps
 3936  3936 16480 pts/29   00:00:00 cat
16480 16480 16480 pts/29   00:00:00 zsh
Run Code Online (Sandbox Code Playgroud)

ksh93

$ cat <(ps -j)
  PID  PGID   SID TTY          TIME CMD
 3946 16480 16480 pts/29   00:00:00 ps
 3947  3947 16480 pts/29   00:00:00 cat
16480 16480 16480 pts/29   00:00:00 ksh
Run Code Online (Sandbox Code Playgroud)

在所有 3 个 shell 中,ps进程与 的进程组位于不同的进程组中cat,后者是终端的前台进程组。zsh至少足以将 stdin 重定向到那里,/dev/null如果它是一个 tty 以避免出现问题,就像许多 shell 对在后台运行的命令所做的那样。

如果 stdin 不是终端,您的命令可以正常工作,但在这里,由于cat不在终端的前台进程组中,它从终端读取意味着它将收到一个SIGTTIN信号,导致它被挂起。而这在这里处理得并不优雅。在您的情况下,它似乎SIGTTIN被忽略或阻止,因为您会收到 EIO 错误(当您不在其前台进程组和 ignore/block 中尝试从控制终端读取时会收到该错误SIGTTIN)。

(head <(cat <&3)) 3<&0
Run Code Online (Sandbox Code Playgroud)

但是,我们在前台启动了一个子 shell,并且那里的所有进程最终都在同一个进程组中,因此允许从终端读取。有了显式重定向zsh,我们就绕过了zsh从 的重定向/dev/null。与其他贝壳,

(head <(cat))
Run Code Online (Sandbox Code Playgroud)

也会工作。