为什么`cat<(cat)`生成EIO?

时间:2017-03-17 05:35:23

标签: bash io stdin subshell process-substitution

我有一个程序可以同时读取两个输入文件。我希望从标准输入中读取该程序。我以为我会用这样的东西:

$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是否未将stdin连接到子shell? :(

有解决方案吗?我特别需要使用进程替换(... <(...)格式),因为$program1tail,偶然)需要文件,我需要进行一些预处理(使用od)在我将其传递给tail之前的标准输入 - 我无法指定/dev/stdin等。

编辑:

我真正想要做的是从文件中读取(另一个进程将写入),而我从标准输入读取,所以我可以接受命令等。我希望我能做到

tail -f <(od -An -vtd1 -w1) <(cat fifo)

同时从标准输入读取FIFO并将其放入单个stdout流中,我可以通过awk(或类似)运行。我知道我可以用任何脚本语言轻松解决这个问题,但我喜欢学习如何让bash做任何事情:P

编辑2:我已经a new question更全面地解释了我刚才所描述的背景。

2 个答案:

答案 0 :(得分:10)

1。解释cat <(cat)生成EIO

的原因

我使用的是Debian Linux 8.7,Bash 4.4.12

让我们用长期<(cat)替换<(sleep),看看发生了什么。

来自 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,第2版):

  1. TPGID29999表示cat(PID 29999)是正在控制终端(pts/14)的前台进程组。 sleep位于后台流程组(PGID 906)。
  2. 906的进程组现在是孤立进程组,因为&#34;每个成员的父级本身是该组的成员或不是小组会议的成员&#34; 。 (PID 906的PPID为903903位于不同的会话中。)
  3. 当孤立后台进程组中的进程从其控制终端读取时,read()将失败并显示EIO
  4. 2。解释为什么cat <(cat)有时有效(不是真的!)

    在操作系统X上使用Bash cat <(cat)的{​​{1}} 工作的评论中提及了

    Daniel Voina。我刚刚在Linux上使用Bash 3.2.57重现了它。

    来自 pty#1

    4.4.12

    (我的答案的第一部分解释了第一个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)。)

    转到另一个 pty#2

    EIO

    让我们看看发生了什么:

    1. 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(PID tac)是正在控制终端的前台进程组(10805 )。 pts/0(PID cat)位于后台流程组(PGID 10806)。

    2. 但这次pgrp 10803未被孤立,因为其成员PID 1080310803)的父级(PID bash,{ {1}})位于另一个pgrp(PGID 10732)中并且它位于同一会话中(SID bash)。

    3. 根据APUE book,当(非孤立的)后台进程组中的进程尝试读取时,终端驱动程序将生成10732 &#34;来自其控制终端&#34; 。因此,当10732读取标准输入时,SIGTTIN将被发送给它,默认情况下,此信号将停止该过程。这就是为什么cat SIGTTIN列在cat输出中显示为STAT(已停止)的原因。由于它停止了我们从键盘输入的数据根本不会发送到它。所以它只是看起来像它正在工作,但事实并非如此。

    4. 结论:

      因此,不同的行为(Tps)取决于当前的Bash是否是会话领导者。 (在我的回答的第一部分中,PID EIO的bash是会话领导者,但第二部分中PID SIGTTIN的bash不是会话领导者。)

答案 1 :(得分:1)

被接受的答案解释了原因,但是我看到了可以解决该问题的一种解决方案。通过用其他()对其进行外壳处理,例如:

(cat <(cat))

请在此处找到解决方案的详细信息: https://unix.stackexchange.com/a/244333/89706