10

我有一个同时从两个输入文件中读取的程序。我想让这个程序从标准输入中读取。我想我会使用这样的东西:

$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

read所以... Linux在系统调用级别上失败了。那很有意思。不是bash将标准输入连接到子外壳吗?:(

有针对这个的解决方法吗?我特别需要使用进程替换(... <(...)格式),因为$program1(tail顺便说一句) 需要文件,并且我需要od对标准输入进行一些预处理 (with ),然后才能将其传递给tail- 我不能只指定/dev/stdin等。

编辑:

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

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

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

编辑 2:我问了一个新问题,更充分地解释了我刚才描述的上下文。

4

2 回答 2

12

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 , 2nd edition):

  1. TPGID存在29999表明cat(PID 29999) 是现在控制终端 ( pts/14) 的前台进程组。并且sleep在后台进程组(PGID 906)中。
  2. 进程组906现在是一个孤立的进程组,因为“每个成员的父进程本身要么是该组的成员,要么不是该组会话的成员”。(PID906的 PPID 是903并且903在不同的会话中。)
  3. 当孤立后台进程组中的进程从其控制终端读取时,read()将失败并显示EIO.

2. 解释为什么cat <(cat)有时有效(不是真的!)

Daniel Voina在使用 Bash 在 OS X 上cat <(cat) 工作的评论中提到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#

让我们看看发生了什么:

  1. TPGID存在10805表明tac(PID 10805) 是现在控制终端 ( pts/0) 的前台进程组。并且cat(PID 10806) 在后台进程组 (PGID 10803) 中。

  2. 但是这次 pgrp10803不是孤立的,因为它的成员 PID 10803( bash) 的父级 (PID 10732, bash) 在另一个 pgrp (PGID 10732) 中并且它在同一个会话 (SID 10732) 中。

  3. 根据APUE bookSIGTTIN“当(非孤立)后台进程组中的进程尝试从其控制终端读取时由终端驱动程序生成”。因此,当cat读取标准输入时,SIGTTIN将发送给它,默认情况下,此信号将停止进程。这就是为什么cat'STATT在输出中显示为 (stopped) ps。由于它已停止,我们从键盘输入的数据根本不会发送给它。所以它看起来像它在工作,但它不是真的。

结论:

所以不同的行为(EIOvs. SIGTTIN)取决于当前的 Bash 是否是会话负责人。(在我回答的第一部分中,PID 的 bash906是会话负责人,但10803第二部分中 PID 的 bash 不是会话负责人。)

于 2017-03-17T07:37:35.410 回答
1

接受的答案解释了原因,但我看到了一种可以解决它的解决方案。它是通过附加的子壳来实现的(),例如:

(cat <(cat))

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

于 2019-10-10T12:56:16.473 回答