리눅스에서 "누수"파이프


12

다음과 같은 파이프 라인이 있다고 가정 해 봅시다.

$ a | b

경우 b정지 후에는, 표준 입력을 처리하는 파이프가 채워질 때와 쓰기는에서 a(하나의 표준 출력까지, 차단 b시작 다시 처리하거나 다이).

이것을 피하고 싶다면 더 큰 파이프 (또는 더 간단하게 buffer(1))를 사용하고 싶을 것입니다.

$ a | buffer | b

이것은 단순히 더 많은 시간을 사게 a될 것이지만 결국에는 멈출 것입니다.

내가 원하는 것은 (내가 다루고있는 매우 구체적인 시나리오의 경우) 가득 찬 경우 버퍼에서 일부 데이터 (이상적으로 한 줄씩)를 삭제하여 a계속 진행할 수있는 "누설"파이프를 갖는 것입니다. 처리 (당신이 상상할 수 있듯이 파이프에 흐르는 데이터는 소모품입니다. 즉, 데이터를 처리하는 b것이 a차단없이 실행할 수있는 것보다 덜 중요합니다 ).

요약하면 경계가 새는 버퍼와 같은 것을 갖고 싶습니다.

$ a | leakybuffer | b

아마도 어떤 언어로도 쉽게 구현할 수있을 것입니다. 내가 놓친 "사용 준비가 된"(또는 bash one-liner와 같은) 것이 있는지 궁금합니다.

참고 : 예제에서 나는 일반 파이프를 사용하고 있지만 질문은 명명 된 파이프에도 동일하게 적용됩니다


아래 답변을 수여했지만 아래의 간단한 솔루션에는 몇 가지 제한 사항이 있기 때문에 leakybuffer 명령을 구현하기로 결정했습니다. https://github.com/CAFxX/leakybuffer


명명 된 파이프가 실제로 채워 집니까? 나는 명명 된 파이프 가 이것에 대한 해결책 이라고 생각했을 것입니다 . 그러나 나는 확실히 말할 수 없었습니다.
와일드 카드

3
명명 된 파이프는 기본적으로 명명되지 않은 파이프와 동일한 용량을
갖습니다.

답변:


14

가장 쉬운 방법은 비 블로킹 출력을 설정하는 일부 프로그램을 통해 파이프하는 것입니다. 다음은 간단한 perl oneliner입니다 (leakybuffer로 저장할 수 있음 ).

그래서 당신 a | b은됩니다 :

a | perl -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | b

무엇인가는 입력을 읽고 출력에 쓰기 (와 동일 cat(1))이지만 출력은 비 블로킹입니다. 즉, 쓰기에 실패하면 오류를 반환하고 데이터를 잃을 것이지만 프로세스는 다음 입력 줄을 계속 진행합니다. 오류. 프로세스는 원하는대로 일종의 라인 버퍼링되지만 아래의주의 사항을 참조하십시오.

예를 들어 다음과 같이 테스트 할 수 있습니다.

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | \
    while read a; do echo $a; done > output

다음 output과 같이 잃어버린 줄을 가진 파일을 얻을 것입니다 (정확한 출력은 쉘의 속도 등에 달려 있습니다).

12768
12769
12770
12771
12772
12773
127775610
75611
75612
75613

당신은 쉘 후 라인을 잃은 어디에 있는지 12773펄이 충분한 버퍼를 가지고 있지 않았다 -하지만, 또한이 이상 12774\n하지만 위해 한 1277그래서 다음 번호가 - 그냥 쓴 있도록 75610라인의 시작 부분에 시작되지 않는 작은 그것을 만들기 추한.

쓰기가 성공적으로 완료되지 않은 경우 펄을 감지하여 나중에 개선 될 수있다가 나중에 새 라인이 나오는 것을 무시하고 나머지 라인을 플러시하려고 시도하지만 펄 스크립트를 훨씬 더 복잡하게 만들 수 있으므로 관심있는 독자 :)

업데이트 (이진 파일의 경우 ) : 줄 바꾸기로 끝나는 줄 (로그 파일 등)을 처리하지 않는 경우 명령을 약간 변경해야합니다. 그렇지 않으면 펄이 입력에 줄 바꿈 문자가 나타나는 빈도에 따라 많은 양의 메모리를 소비합니다.

perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (read STDIN, $_, 4096) { print }' 

이진 파일에서도 추가 메모리를 사용하지 않고도 올바르게 작동합니다.

업데이트 2-더 나은 텍스트 파일 출력 : 출력 버퍼 피하기 ( syswrite대신 print) :

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { syswrite STDOUT,$_ }' | \
    while read a; do echo $a; done > output

나를 위해 "병합 된 라인"문제를 해결하는 것 같습니다 :

12766
12767
12768
16384
16385
16386

(참고 : perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' outputoneliner 로 어떤 라인 출력이 절단되었는지 확인할 수 있습니다 )


나는 oneliner를 좋아한다 : 나는 perl 전문가가 아니다. 만약 누군가 위의 개선을 제안 할 수 있다면 그것은 대단 할 것이다
CAFxX

1
이것은 어느 정도 작동 하는 것 같습니다 . 그러나 내 명령 인을 보면서 perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000perl은 OOM 관리자가 죽일 때까지 계속 더 많은 메모리를 할당하는 것으로 보입니다.
Ponkadoodle

@Wallacoloo는 내 사례가 로그 파일을 스트리밍하고 있음을 지적 해 주셔서 감사합니다 ... 이진 파일을 지원하는 데 필요한 약간의 변경 사항은 업데이트 된 답변을 참조하십시오.
Matija Nalis 2012

또한 GNU보기 dd'들 dd oflag=nonblock status=none.
Stéphane Chazelas

1
죄송합니다, 내 나쁜 다시 사실 때문에, 원자 보장된다 (POSIX에 의해 적어도 512 일 필요, 리눅스 4096) PIPE_BUF가 바이트보다 작의 기록 $| = 1syswrite()접근 방식은 참으로 긴 줄을 합리적으로 짧은 짧게 쓰기를 방지 않습니다.
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.