【问题标题】:Why does `cat <(cat)` produce EIO?为什么 `cat <(cat)` 会产生 EIO?
【发布时间】:2017-08-08 13:30:56
【问题描述】:

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

$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 没有将标准输入连接到子外壳吗? :(

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

编辑:

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

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

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

编辑 2:我已询问 a new question,以便更全面地解释我刚才描述的上下文。

【问题讨论】:

  • 很有趣,因为它适用于 OS-X。所以不像我最初怀疑的那样来自 bash。这很有趣。
  • @DanielVoina:感谢您提供的信息,我对其他平台很好奇!
  • 我不太了解您的真正需求,因为tailstdin 有点奇怪。希望我的回答能给你一些提示。
  • 在 StackOverflow 上看到这样的问题让我对编程职业有了希望
  • @whjm:非常好的观点。我fully described the context in a new question,并接受了你对这个问题的回答,因为它确实回答了它:)

标签: bash io stdin subshell process-substitution


【解决方案1】:

1。解释为什么cat &lt;(cat) 产生EIO

(我使用的是 Debian Linux 8.7,Bash 4.4.12)

让我们将&lt;(cat) 替换为长期运行的&lt;(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 的进程组现在是一个孤立的进程组,因为“每个成员的父进程本身要么是该组的成员,要么不是该组会话的成员"。 (PID 906 的 PPID 是 903903 在不同的会话中。)
  3. 当孤立后台进程组中的进程从其控制终端读取时,read() 将失败并显示EIO

2。解释为什么cat &lt;(cat) 有时有效(不是真的!)

Daniel Voina 在评论中提到 cat &lt;(cat) works 在 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.

(第一个cat &lt;(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. TPGID10805 表示 tac (PID 10805) 是现在控制终端 (pts/0) 的前台进程组。而cat(PID 10806)在后台进程组(PGID 10803)中。

  2. 1234563 @) 并且它在同一个会话中 (SID 10732)。
  3. 根据APUE bookSIGTTIN“当(非孤立)后台进程组中的进程尝试从其控制终端读取数据时由终端驱动程序生成”时间>。因此,当cat 读取标准输入时,SIGTTIN 将被发送给它,默认情况下,此信号将停止该进程。这就是为什么catSTAT 列在ps 输出中显示为T(已停止)。由于它已停止,我们从键盘输入的数据根本不会发送给它。所以它只是看起来在工作,但实际上并非如此。

结论:

因此不同的行为(EIOSIGTTIN)取决于当前 Bash 是否是会话负责人。 (在我回答的第一部分中,PID 906 的 bash 是会话负责人,但第二部分中 PID 10803 的 bash 不是会话负责人。)

【讨论】:

  • 为什么会这样?什么是 903,我们在什么会话中找到它?
  • 添加了有关 903 的信息。但在这里我们只需要知道它在不同的会话中。
  • a) Headsmash;现在我必须想出第二种方法。 b) 非常感谢您提供这些惊人的信息 :)(PS:GNU sleep 接受未记录的 infinity 参数。)
  • 我从来不知道。不像输入一个大数字那么容易。 :)
  • 愚蠢的问题,但是,为什么我们不能使用 /dev/stdin 并将它与管道结合起来? some_filtering_cmd | real_cmd /dev/stdin &lt;(other_cmd) | tail 之类的东西。
【解决方案2】:

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

(cat &lt;(cat))

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

【讨论】:

  • 感谢您的交叉链接。我当然不会发现这个问题。
猜你喜欢
  • 2021-03-17
  • 2016-09-20
  • 2016-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-28
相关资源
最近更新 更多