sh 脚本,循环直到标准输入关闭 - 我如何测试它?

sh 脚本,循环直到标准输入关闭 - 我如何测试它?

我正在尝试通过管道传递输入流,并希望捕获下游管道程序可能失败的情况,在这种情况下我需要重新启动它。所以我把它放入一个循环中:

step1 |while true ; do step2 ; done

但如果上游管道关闭,那么我希望该循环退出。我无法从步骤2的退出状态看出这一点,否则我可能会说

step1 |until [ $? -ne 0 ] ; do step2 ; done

我需要一个测试来检查标准输入是否已关闭但实际上并未消耗标准输入中的字符。

我正在想如何在 C 中做到这一点。零缓冲区的 read(2) 是否允许我测试这个?我认为不会。选择(2)怎么样?没有。或者确实如此? fcntl(2) 怎么样?我找不到任何东西。

除了消耗一个字节然后以某种方式再次将其输出之外,真的没有其他方法吗?没有关闭文件描述符测试?

答案1

至少在 Linux 上,您可以通过在事件掩码中使用poll()with 来判断管道的另一端是否已关闭。POLLHUP

但请注意,此时管道中可能仍然有数据可供读取,因此您可能还需要检查这一点。再次在 Linux 上,这可以通过 ioctl 来完成FIONREAD

所以你可以定义一个:

stdin_alive() {
  perl -MIO::Poll -e '
    require "sys/ioctl.ph";
    -p STDIN or die "stdin is not a pipe\n";
    $p = IO::Poll->new;
    $p->mask(STDIN, POLLHUP);
    if ($p->poll(0)) {
      ioctl(STDIN, &FIONREAD, $n) or die "FIONREAD: $!\n";
      $n = unpack "L", $n;
      exit 1 unless $n;
    }'
}

并将其用作:

step1 | while stdin_alive; do step2; done

另一种方法是使用ifne以下命令moreutils

step1 |
  while
    ifne sh -c 'step2 && exit 42'
    [ "$?" -eq 42 ]
  do
    continue
  done

ifne尝试读取其标准输入。如果至少读取了一个字节,则ifne启动该命令,并将其标准输入连接到一个新管道,并将其从标准输入通过该新管道读取的内容铲到命令中,因此效率较低,因为存在额外的铲除和传输一个额外的管道,但这意味着无论输入类型如何(不仅仅是管道)它都可以工作。

与之前解决方案的另一个区别是,ifne在开始之前等待 stdin 上的输入step2,而poll()+FIONREAD方法只是在检查的确切时间点检查管道是否处于活动状态,管道是否有数据。不过,可以通过添加POLLIN到正在轮询的事件列表来改变这一点。

相关内容