我有一个程序可以同时读取两个输入文件。我希望从标准输入中读取该程序。我以为我会用这样的东西:
$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? :(
有解决方案吗?我特别需要使用进程替换(... <(...)
格式),因为$program1
(tail
,偶然)需要文件,我需要进行一些预处理(使用od
)在我将其传递给tail
之前的标准输入 - 我无法指定/dev/stdin
等。
编辑:
我真正想要做的是从文件中读取(另一个进程将写入),而我也从标准输入读取,所以我可以接受命令等。我希望我能做到
tail -f <(od -An -vtd1 -w1) <(cat fifo)
同时从标准输入和读取FIFO并将其放入单个stdout流中,我可以通过awk(或类似)运行。我知道我可以用任何脚本语言轻松解决这个问题,但我喜欢学习如何让bash
做任何事情:P
编辑2:我已经a new question更全面地解释了我刚才所描述的背景。
答案 0 :(得分:10)
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版):
TPGID
为29999
表示cat
(PID 29999
)是正在控制终端(pts/14
)的前台进程组。 sleep
位于后台流程组(PGID 906
)。906
的进程组现在是孤立进程组,因为&#34;每个成员的父级本身是该组的成员或不是小组会议的成员&#34; 。 (PID 906
的PPID为903
,903
位于不同的会话中。)read()
将失败并显示EIO
。cat <(cat)
有时有效(不是真的!)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
让我们看看发生了什么:
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
)。
但这次pgrp 10803
未被孤立,因为其成员PID 10803
(10803
)的父级(PID bash
,{ {1}})位于另一个pgrp(PGID 10732
)中并且它位于同一会话中(SID bash
)。
根据APUE book,当(非孤立的)后台进程组中的进程尝试读取时,终端驱动程序将生成10732
&#34;来自其控制终端&#34; 。因此,当10732
读取标准输入时,SIGTTIN
将被发送给它,默认情况下,此信号将停止该过程。这就是为什么cat
SIGTTIN
列在cat
输出中显示为STAT
(已停止)的原因。由于它停止了我们从键盘输入的数据根本不会发送到它。所以它只是看起来像它正在工作,但事实并非如此。
因此,不同的行为(T
与ps
)取决于当前的Bash是否是会话领导者。 (在我的回答的第一部分中,PID EIO
的bash是会话领导者,但第二部分中PID SIGTTIN
的bash不是会话领导者。)
答案 1 :(得分:1)
被接受的答案解释了原因,但是我看到了可以解决该问题的一种解决方案。通过用其他()
对其进行外壳处理,例如:
(cat <(cat))
请在此处找到解决方案的详细信息: https://unix.stackexchange.com/a/244333/89706