I have a program that reads from two input files simultaneously. I'd like to have this program read from standard input. I thought I'd use something like this:
$program1 <(cat) <($program2)
but I've just discovered that
cat <(cat)
produces
....
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
and similarly,
$ cat <(read -n 1)
bash: read: read error: 0: Input/output error
So... Linux is failing to read
at the syscall level. That's interesting. Is bash
not wiring stdin to the subshell? :(
Is there a solution to this? I specifically need to use process substitution (the ... <(...)
format) because $program1
(tail
, incidentally) expects files, and I need to do some preprocessing (with od
) on standard input before I can pass it to tail
- I can't just specify /dev/stdin
et al.
EDIT:
What I actually want to do is read from a file (which another process will be writing to) while I also read from standard input so I can accept commands and such. I was hoping I could do
tail -f <(od -An -vtd1 -w1) <(cat fifo)
to read from standard input and the FIFO simultaneously and drop that into a single stdout stream I could run through awk (or similar). I know I could solve this trivially in any scripting language, but I like learning how to make bash
do everything :P
EDIT 2: I've asked a new question that more fully explains the context I described just above.
cat <(cat)
produces EIO
( I'm using Debian Linux 8.7, Bash 4.4.12 )
Let's replace <(cat)
with the long running <(sleep)
to see what's happening.
From pty #1:
$ echo $$
906
$ tty
/dev/pts/14
$ cat <(sleep 12345)
Go to another 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
$
Let me explain it (according to the APUE book, 2nd edition):
TPGID
being 29999
indicates that cat
(PID 29999
) is the foreground process group which is now controlling the terminal (pts/14
). And sleep
is in the background process group (PGID 906
).906
is now an orphaned process group because "the parent of every member is either itself a member of the group or is not a member of the group’s session". (The PID 906
's PPID is 903
and 903
is in a different session.)read()
would fail with EIO
.cat <(cat)
sometimes works (not really!)Daniel Voina mentioned in a comment that cat <(cat)
works on OS X with Bash 3.2.57
. I just managed to reproduce it also on Linux with Bash 4.4.12
.
From 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.
(The first cat <(cat)
failing with EIO
was explained in the first part of my answer.)
Go to another 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#
Let's see what's happening:
The TPGID
being 10805
indicates that tac
(PID 10805
) is the foreground process group which is now controlling the terminal (pts/0
). And cat
(PID 10806
) is in the background process group (PGID 10803
).
But this time the pgrp 10803
is not orphanded because its member PID 10803
(bash
)'s parent (PID 10732
, bash
) is in another pgrp (PGID 10732
) and it's in the same session (SID 10732
).
According to the APUE book, SIGTTIN
will be "generated by the terminal driver when a process in a (non-orphaned) background process group tries to read from its controlling terminal". So when cat
reads stdin, SIGTTIN
will be sent to it and by default this signal would stop the process. That's why the cat
's STAT
column was shown as T
(stopped) in the ps
output. Since it's stopped the data we input from keyboard are not sent to it at all. So it just looks like it's working but it's not really.
So the different behaviors (EIO
vs. SIGTTIN
) depend on whether the current Bash is a session leader or not. (In the 1st part of my answer, the bash of PID 906
is the session leader, but the bash of PID 10803
in the 2nd part is not the session leader.)
The accepted answer explained why, but I saw one solution which can solve it. It is by subshelling it with additional ()
, such as:
(cat <(cat))
Please find the solution details here: https://unix.stackexchange.com/a/244333/89706
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With