すでにパイプバッファ内のすべての内容を読み、それを別のプログラムにパイプしてから、追加の入力を待たずにすぐに終了する方法はありますか?
バイナリ安全でなければなりません。追加プログラムのインストールを要求しても大丈夫です。
たとえば、次のコマンドは2番目のエコーでSIGPIPEを出力してa
発生b
させないでください(少なくともシステムに負荷があまりない場合)。
{ echo a; sleep 2s; echo b;} | { sleep 1s; my_command;}
答え1
パイプに対してシステムコールを実行すると、パイプread()
に何もない場合は中止され、そうでない場合はパイプの内容がすぐに返されます。
今は中には何が含まれていますか?、少なくともLinux 4.4(他のシステムまたは他のLinuxバージョンの場合はYMMV)を使用するamd64マルチコアシステムでは、write()
1つ以上のプロセスが現在他の端で実行されているか、別の書き込みシステム呼び出しの場合は容量を超えることがあります。パイプ容量(現在のLinuxバージョンではデフォルトでは64KiB)、スケジューラはread()
システムコール中にパイプから何度も読み取るプロセスとこれらのプロセスの間を行き来し、返されたread()
値はパイプ容量をはるかに超える可能性があります。 。
このdd
コマンドはシステムコールへのCLIインタフェースですread
。
dd bs=1G count=1
(すべてのdd
実装がこれらのサフィックスをG
サポートしているわけではありません。)read()
stdinで1GiBサイズの操作を実行します(次にwrite()
stdoutから1が続きます)。
GNUを使用するdd
とiflag=nonblock
。これにより、パイプは非遮断モードに設定されますが、後で非遮断状態に保たれるため、望ましくない可能性があります。たとえば、
(date; sleep 2; date) |
(sleep 1
dd bs=1G count=1 status=none iflag=nonblock
wc -c)
与える:
Mon 23 Jan 22:11:30 GMT 2017
wc: 'standard input': Resource temporarily unavailable
0
パイプ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
(そして、ある実行から次の実行まで異なる数字が得られます)。
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
}'
2つのステップで実行されるため、他のプロセスが同時にパイプから読み取られる場合、他のプロセスがその間にパイプを空にすると、まだブロックされる可能性がありioctl()
ますread()
。
答え2
単純なCプログラムを使用すると、実際に待たずに読み書きを終了するstdin
ことができ、パイプラインチェーンの中間段階として使用できます。stdout
EOF
簡単な例は次のとおりです。
メインプログラム
#include <stdio.h>
#include <unistd.h>
#define BUFFER_SIZE 1024
int main(int argc, char **argv){
char buffer[BUFFER_SIZE];
ssize_t read_size;
//Read up to BUFFER_SIZE bytes of what's currently at the stdin
read_size = read(STDIN_FILENO, buffer, BUFFER_SIZE);
if(read_size > 0){
write(STDOUT_FILENO, buffer, read_size);
}
return 0;
}
を使用してプログラムをコンパイルしますgcc -o test main.c
。
その後、実行すると、{ echo a; sleep 2s; echo b; } | { sleep 1s; ./test; } | { ./mycommand; }
文字を読み取るときに唯一の文字であるmycommand
ため、その文字だけを受け取ります。a
stdin
./test
これを確認するために実行すると画面に表示されます{ echo a; sleep 2s; echo b; } | { sleep 1s; ./test; } | cat
。a