이미 파이프 버퍼에 있는 모든 내용을 읽고 이를 다른 프로그램으로 파이프한 다음 추가 입력을 기다리지 않고 즉시 종료할 수 있는 방법이 있습니까?
바이너리 안전이어야 합니다. 추가 프로그램 설치를 요청해도 괜찮습니다.
예를 들어, 다음 명령은 두 번째 에코에서 SIGPIPE를 출력 a
하고 b
발생시키지 않아야 합니다(적어도 시스템에 너무 많은 로드가 없는 경우).
{ echo a; sleep 2s; echo b;} | { sleep 1s; my_command;}
답변1
파이프에 대해 시스템 호출을 수행하는 경우 read()
파이프에 아무것도 없으면 중단되고, 그렇지 않으면 파이프의 내용이 즉시 반환됩니다.
지금은안에 무엇이 들어있나요?, 적어도 Linux 4.4(다른 시스템 또는 다른 Linux 버전의 경우 YMMV)를 사용하는 amd64 멀티 코어 시스템에서는 write()
하나 이상의 프로세스가 현재 다른 쪽 끝에서 실행 중이거나 다른 쓰기 시스템 호출인 경우 용량을 초과할 수 있습니다. 파이프 용량(현재 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
}'
두 단계로 수행되기 때문에 다른 프로세스가 동시에 파이프에서 읽는 경우 다른 프로세스가 그 사이에 파이프를 비우면 여전히 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