为什么 `cat <(cat)` 会产生 EIO?
Why does `cat <(cat)` produce EIO?
我有一个程序可以同时读取两个输入文件。我想从标准输入中读取这个程序。我以为我会使用这样的东西:
$program1 <(cat) <($program2)
但我刚刚发现
cat <(cat)
产生
....
mmap2(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb758e000
read(0, 0xb758f000, 131072) = -1 EIO (Input/output error)
....
cat: -: Input/output error
同样,
$ cat <(read -n 1)
bash: read: read error: 0: Input/output error
所以... Linux 在系统调用级别 read
失败。那很有意思。 bash
没有将标准输入连接到子外壳吗? :(
有解决办法吗?我特别需要使用进程替换(... <(...)
格式),因为 $program1
(tail
,顺便说一下)需要文件,我需要做一些预处理(使用 od
)在我将它传递给 tail
之前的标准输入 - 我不能只指定 /dev/stdin
等
编辑:
我真正想做的是从文件中读取(另一个进程将写入该文件),而我还从标准输入中读取以便我可以接受命令等。我希望我能做到
tail -f <(od -An -vtd1 -w1) <(cat fifo)
同时从标准输入 和 FIFO 读取并将其放入单个标准输出流中,我可以 运行 通过 awk(或类似的)。我知道我可以用任何脚本语言轻松解决这个问题,但我喜欢学习如何让 bash
做所有事情 :P
编辑 2:我问 a new question 更全面地解释了我在上面描述的上下文。
1。解释为什么 cat <(cat)
产生 EIO
( 我正在使用 Debian Linux 8.7, Bash 4.4.12 )
让我们用长 运行 <(sleep)
替换 <(cat)
看看发生了什么。
来自 pty #1:
$ echo $$
906
$ tty
/dev/pts/14
$ cat <(sleep 12345)
转到另一个 pty #2:
$ ps t pts/14 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
903 906 906 906 pts/14 29999 Ss 0 0:00 bash
906 29998 906 906 pts/14 29999 S 0 0:00 bash
29998 30000 906 906 pts/14 29999 S 0 0:00 sleep 12345
906 29999 29999 906 pts/14 29999 S+ 0 0:00 cat /dev/fd/63
$ ps p 903 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 903 903 903 ? -1 Ss 0 0:07 SCREEN -T linux -U
$
我来解释一下(根据APUE book,第二版):
-
TPGID
为 29999
表示 cat
(PID 29999
) 是现在控制终端的前台进程组 (pts/14
) .而sleep
在后台进程组(PGID906
).
906
的进程组现在是一个孤立进程组,因为"the parent of every member is either itself a member of the group or is not a member of the group’s session"。 (PID 906
的 PPID 是 903
而 903
在不同的会话中。)
- 当孤立的后台进程组中的进程从其控制终端读取时,
read()
将失败并显示 EIO
。
2。解释为什么 cat <(cat)
有时 有效 (不是真的!)
Daniel Voina 在评论中提到 cat <(cat)
在 OS X 上 与 Bash 3.2.57
有效。我刚刚也在 Linux 上用 Bash 4.4.12
.
重现了它
来自 pty #1:
bash-4.4# echo $$
10732
bash-4.4# tty
/dev/pts/0
bash-4.4# cat <(cat)
cat: -: Input/output error
bash-4.4#
bash-4.4#
bash-4.4# bash --norc --noprofile # start a new bash
bash-4.4# tac <(cat)
<-- It's waiting here so looks like it's working.
(第一个 cat <(cat)
因 EIO
失败已在我的回答的第一部分进行了解释。)
转到另一个 pty #2:
bash-4.4# ps t pts/0 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
10527 10732 10732 10732 pts/0 10805 Ss 0 0:00 bash
10732 10803 10803 10732 pts/0 10805 S 0 0:00 bash --norc --noprofile
10803 10804 10803 10732 pts/0 10805 S 0 0:00 bash --norc --noprofile
10804 10806 10803 10732 pts/0 10805 T 0 0:00 cat
10803 10805 10805 10732 pts/0 10805 S+ 0 0:00 tac /dev/fd/63
bash-4.4# ps p 10527 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
10526 10527 10527 10527 ? -1 Ss 0 0:00 SCREEN -T dtterm -U
bash-4.4#
让我们看看发生了什么:
TPGID
为 10805
表示 tac
(PID 10805
) 是前台进程组,现在正在控制终端 (pts/0
).而cat
(PID10806
)在后台进程组(PGID10803
).
但这次 pgrp 10803
不是孤立的,因为它的成员 PID 10803
(bash
) 的父级 (PID 10732
, bash
) 在另一个 pgrp (PGID 10732
) 中并且在同一个会话 (SID 10732
) 中。
根据APUE book,SIGTTIN
将是"generated by the terminal driver when a process in a (non-orphaned) background process group tries to read from its controlling terminal"。因此,当 cat
读取标准输入时,SIGTTIN
将发送给它,默认情况下,此信号将 停止 进程。这就是 cat
的 STAT
列在 ps
输出中显示为 T
(已停止)的原因。由于它已停止,我们从键盘输入的数据根本不会发送给它。所以它只是 看起来像 它在工作,但实际上不是。
结论:
因此不同的行为(EIO
与 SIGTTIN
)取决于当前 Bash 是否是会话领导者。 (在我的回答的第一部分,PID 906
的bash是session leader,但是第二部分PID 10803
的bash不是session leader .)
接受的答案解释了原因,但我看到了一个可以解决它的解决方案。它是通过附加 ()
对其进行子壳化,例如:
(cat <(cat))
请在此处找到解决方案的详细信息:
https://unix.stackexchange.com/a/244333/89706
我有一个程序可以同时读取两个输入文件。我想从标准输入中读取这个程序。我以为我会使用这样的东西:
$program1 <(cat) <($program2)
但我刚刚发现
cat <(cat)
产生
....
mmap2(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb758e000
read(0, 0xb758f000, 131072) = -1 EIO (Input/output error)
....
cat: -: Input/output error
同样,
$ cat <(read -n 1)
bash: read: read error: 0: Input/output error
所以... Linux 在系统调用级别 read
失败。那很有意思。 bash
没有将标准输入连接到子外壳吗? :(
有解决办法吗?我特别需要使用进程替换(... <(...)
格式),因为 $program1
(tail
,顺便说一下)需要文件,我需要做一些预处理(使用 od
)在我将它传递给 tail
之前的标准输入 - 我不能只指定 /dev/stdin
等
编辑:
我真正想做的是从文件中读取(另一个进程将写入该文件),而我还从标准输入中读取以便我可以接受命令等。我希望我能做到
tail -f <(od -An -vtd1 -w1) <(cat fifo)
同时从标准输入 和 FIFO 读取并将其放入单个标准输出流中,我可以 运行 通过 awk(或类似的)。我知道我可以用任何脚本语言轻松解决这个问题,但我喜欢学习如何让 bash
做所有事情 :P
编辑 2:我问 a new question 更全面地解释了我在上面描述的上下文。
1。解释为什么 cat <(cat)
产生 EIO
( 我正在使用 Debian Linux 8.7, Bash 4.4.12 )
让我们用长 运行 <(sleep)
替换 <(cat)
看看发生了什么。
来自 pty #1:
$ echo $$
906
$ tty
/dev/pts/14
$ cat <(sleep 12345)
转到另一个 pty #2:
$ ps t pts/14 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
903 906 906 906 pts/14 29999 Ss 0 0:00 bash
906 29998 906 906 pts/14 29999 S 0 0:00 bash
29998 30000 906 906 pts/14 29999 S 0 0:00 sleep 12345
906 29999 29999 906 pts/14 29999 S+ 0 0:00 cat /dev/fd/63
$ ps p 903 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 903 903 903 ? -1 Ss 0 0:07 SCREEN -T linux -U
$
我来解释一下(根据APUE book,第二版):
-
TPGID
为29999
表示cat
(PID29999
) 是现在控制终端的前台进程组 (pts/14
) .而sleep
在后台进程组(PGID906
). 906
的进程组现在是一个孤立进程组,因为"the parent of every member is either itself a member of the group or is not a member of the group’s session"。 (PID906
的 PPID 是903
而903
在不同的会话中。)- 当孤立的后台进程组中的进程从其控制终端读取时,
read()
将失败并显示EIO
。
2。解释为什么 cat <(cat)
有时 有效 (不是真的!)
Daniel Voina 在评论中提到 cat <(cat)
在 OS X 上 与 Bash 3.2.57
有效。我刚刚也在 Linux 上用 Bash 4.4.12
.
来自 pty #1:
bash-4.4# echo $$
10732
bash-4.4# tty
/dev/pts/0
bash-4.4# cat <(cat)
cat: -: Input/output error
bash-4.4#
bash-4.4#
bash-4.4# bash --norc --noprofile # start a new bash
bash-4.4# tac <(cat)
<-- It's waiting here so looks like it's working.
(第一个 cat <(cat)
因 EIO
失败已在我的回答的第一部分进行了解释。)
转到另一个 pty #2:
bash-4.4# ps t pts/0 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
10527 10732 10732 10732 pts/0 10805 Ss 0 0:00 bash
10732 10803 10803 10732 pts/0 10805 S 0 0:00 bash --norc --noprofile
10803 10804 10803 10732 pts/0 10805 S 0 0:00 bash --norc --noprofile
10804 10806 10803 10732 pts/0 10805 T 0 0:00 cat
10803 10805 10805 10732 pts/0 10805 S+ 0 0:00 tac /dev/fd/63
bash-4.4# ps p 10527 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
10526 10527 10527 10527 ? -1 Ss 0 0:00 SCREEN -T dtterm -U
bash-4.4#
让我们看看发生了什么:
TPGID
为10805
表示tac
(PID10805
) 是前台进程组,现在正在控制终端 (pts/0
).而cat
(PID10806
)在后台进程组(PGID10803
).但这次 pgrp
10803
不是孤立的,因为它的成员 PID10803
(bash
) 的父级 (PID10732
,bash
) 在另一个 pgrp (PGID10732
) 中并且在同一个会话 (SID10732
) 中。根据APUE book,
SIGTTIN
将是"generated by the terminal driver when a process in a (non-orphaned) background process group tries to read from its controlling terminal"。因此,当cat
读取标准输入时,SIGTTIN
将发送给它,默认情况下,此信号将 停止 进程。这就是cat
的STAT
列在ps
输出中显示为T
(已停止)的原因。由于它已停止,我们从键盘输入的数据根本不会发送给它。所以它只是 看起来像 它在工作,但实际上不是。
结论:
因此不同的行为(EIO
与 SIGTTIN
)取决于当前 Bash 是否是会话领导者。 (在我的回答的第一部分,PID 906
的bash是session leader,但是第二部分PID 10803
的bash不是session leader .)
接受的答案解释了原因,但我看到了一个可以解决它的解决方案。它是通过附加 ()
对其进行子壳化,例如:
(cat <(cat))
请在此处找到解决方案的详细信息: https://unix.stackexchange.com/a/244333/89706