i33*_*36_ 10 io bash stdin subshell process-substitution
我有一个程序可以同时读取两个输入文件.我想从标准输入中读取该程序.我以为我会用这样的东西:
$program1 <(cat) <($program2)
Run Code Online (Sandbox Code Playgroud)
但我刚刚发现了
cat <(cat)
Run Code Online (Sandbox Code Playgroud)
产生
....
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
Run Code Online (Sandbox Code Playgroud)
同样地,
$ cat <(read -n 1)
bash: read: read error: 0: Input/output error
Run Code Online (Sandbox Code Playgroud)
所以...... Linux无法进入read系统调用级别.那很有意思.是bash不是将stdin连接到子壳?:(
这个问题有方法解决吗?我特别需要使用进程替换(... <(...)格式),因为$program1(tail顺便说一下)需要文件,我需要od在标准输入上做一些预处理(with )才能传递给它tail- 我不能只指定/dev/stdin等等.
编辑:
我真正想要做的是从一个文件(另一个进程将写入)读取,同时我也从标准输入读取,所以我可以接受命令等.我希望我能做到
tail -f <(od -An -vtd1 -w1) <(cat fifo)
Run Code Online (Sandbox Code Playgroud)
同时从标准输入和 FIFO中读取并将其放入单个stdout流中,我可以通过awk(或类似的)运行.我知道我可以用任何脚本语言解决这个问题,但我喜欢学习如何bash做任何事情:P
编辑2:我问了一个新问题,更全面地解释了我刚才描述的背景.
pyn*_*exj 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)
Run Code Online (Sandbox Code Playgroud)
去另一个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
$
Run Code Online (Sandbox Code Playgroud)
让我解释一下(根据APUE的书,第2版):
TPGID存在29999表明,cat(PID 29999)是目前正在控制终端前台进程组(pts/14).并且sleep是在后台进程组(PGID 906)中.906现在是一个孤立的进程组,因为"每个成员的父成员本身就是该成员的成员或者不是该成员的成员".(PID 906的PPID是903和903不同的会话.)read()将失败EIO.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.
Run Code Online (Sandbox Code Playgroud)
(我的答案的第一部分解释了第一个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#
Run Code Online (Sandbox Code Playgroud)
让我们看看发生了什么:
的TPGID存在10805表明,tac(PID 10805)是目前正在控制终端前台进程组(pts/0).并且cat(PID 10806)在后台进程组(PGID 10803)中.
但是这次pgrp 10803不是孤立的,因为它的成员PID 10803(bash)的父(PID 10732,bash)在另一个pgrp(PGID 10732)中并且它在同一个会话(SID 10732)中.
根据APUE书,SIGTTIN将"当(非孤立的)后台进程组中的进程试图从其控制终端读取时由终端驱动程序生成".因此,当cat读取stdin时,SIGTTIN将被发送到它,并且默认情况下此信号将停止该过程.这就是为什么cat的STAT柱显示为T中(停止)ps输出.由于它已停止,我们从键盘输入的数据根本不会发送到它.所以看起来它看起来很有效,但事实并非如此.
因此,不同的行为(EIOvs. SIGTTIN)取决于当前Bash是否是会话领导者.(在我的回答的第一部分中,PID的bash 906是会话的领导者,但是10803第二部分中的PID的bash 不是会话的领导者.)