无需等待即可读取管道缓冲区中的所有内容

use*_*013 6 pipe shell-script buffer

有没有办法读取管道缓冲区中已有的所有内容,将它们通过管道传输到另一个程序,然后立即退出而无需等待更多输入?

它必须是二进制安全的。可以要求安装其他程序。

例如,下面的命令应该输出aand not b,并在第二个回声上引发 SIGPIPE (至少如果系统没有太多负载):

{ echo a; sleep 2s; echo b;} | { sleep 1s; my_command;}
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 3

如果您read()在管道上执行一个系统调用,如果管道中没有任何内容,它将挂起,否则会立即返回其中的内容。

现在,对于其中的内容,至少在我的 amd64 多核系统上使用 Linux 4.4(对于其他系统或其他版本的 Linux 来说是 YMMV),如果一个或多个进程当前正在write()另一端执行(或其他写入系统调用),可能超过管道的容量(当前版本的 Linux 上默认为 64KiB),调度程序将在read()系统调用期间在这些进程和从管道读取的进程之间来回多次,并且read()返回的值可能比管道的容量大得多。管道容量。

dd命令是系统调用的 CLI 接口read

dd bs=1G count=1
Run Code Online (Sandbox Code Playgroud)

(请注意,并非所有dd实现都支持这些G后缀)read()从 stdin 执行大小为 1GiB 的操作(后跟write()stdout 上的一个)

使用 GNU dd,您可以使用 避免空管道阻塞iflag=nonblock。这会将管道设置为非阻塞模式,但请注意,它会使其之后保持非阻塞,这可能是不可取的。例如:

(date; sleep 2; date) |
  (sleep 1
   dd bs=1G count=1 status=none iflag=nonblock
   wc -c)
Run Code Online (Sandbox Code Playgroud)

给:

Mon 23 Jan 22:11:30 GMT 2017
wc: 'standard input': Resource temporarily unavailable
0
Run Code Online (Sandbox Code Playgroud)

随着管道也变得不阻塞wc

正如前面所说,无论有没有nonblock,您最终都会读到超出管道所能容纳的内容:

$ (cat /dev/zero & cat /dev/zero & cat /dev/zero) |
     (sleep 1; dd bs=1G count=1) | wc -c
0+1 records in
0+1 records out
545914880 bytes (546 MB, 521 MiB) copied, 0.48251 s, 1.1 GB/s
545914880
Run Code Online (Sandbox Code Playgroud)

(并且从一次运行到下一次运行你会得到不同的数字)。

支持它的系统上的另一种方法是在执行读取之前使用FIONREAD ioctl()查询管道中有多少数据。

perl -e '
  require "sys/ioctl.ph";
  ioctl(STDIN, &FIONREAD, $n) or die "ioctl: $!\n";
  $n = unpack "L", $n;
  if ($n) {
    sysread STDIN, $text, $n or die "read: $!\n";
    print $text
  }'
Run Code Online (Sandbox Code Playgroud)

请注意,因为它是分两步完成的,所以如果另一个进程同时从管道中读取数据,那么如果另一个进程清空 和 之间的管道,它可能仍然会ioctl()阻塞read()