파이프, 파이프 라인에서 데이터 흐름은 어떻게됩니까?


22

파이프 라인에서 데이터가 어떻게 흐르는 지 이해하지 못하고 누군가가 무슨 일이 일어나고 있는지 명확히 할 수 있기를 바랍니다.

명령 파이프 라인이 파일 (텍스트, 문자열 배열)을 한 줄씩 처리한다고 생각했습니다. (각 명령 자체가 한 줄씩 작동하는 경우) 각 텍스트 행이 파이프 라인을 통과하면 명령은 이전 입력이 전체 입력 처리를 완료 할 때까지 기다리지 않습니다.

그러나 그렇지 않은 것 같습니다.

다음은 테스트 예입니다. 몇 줄의 텍스트가 있습니다. 나는 대문자로 각 줄을 두 번 반복합니다. 나는 그렇게한다 cat text | tr '[:lower:]' '[:upper:]' | sed 'p'.

프로세스를 따르기 위해 "대화식으로"실행할 수 있습니다 cat. 에서 입력 파일 이름을 건너 뛰십시오 . 파이프 라인의 각 부분은 한 줄씩 실행됩니다.

$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2

그러나 완전한 파이프 라인은 입력을 마치고 EOF결과를 인쇄 할 때까지 기다립니다 .

$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D

그렇게되어 있습니까? 왜 한 줄씩 표시되지 않습니까?


파이프가 아니며 catstdin이 닫힐 때까지 버퍼링됩니다.
goldilocks

하지만 trsed에서 공정 라인을 catstdin을 닫히기 전에
xealits

stdio (내가 언급 한 모든 프로그램에서 사용한다고 생각)가 사용하는 기본값은 stderr이 버퍼링되지 않고 stdout이 터미널에 쓸 때 라인 버퍼링되고 그렇지 않으면 완전히 버퍼링된다는 것입니다 (예 : 파일 또는 파이프에 쓰는 경우) . 일부 명령에는 stdout 버퍼링을 변경할 수있는 플래그가 있지만 tr이 아닌 것처럼 보입니다.
kasperd

답변:


36

일반적인 버퍼링 규칙과 stdio대부분의 유닉스 프로그램에서 사용 하는 C 표준 I / O 라이브러리 ( )가 있습니다. 출력이 터미널로가는 경우 각 라인의 끝에서 플러시됩니다. 그렇지 않으면 버퍼 (내 Linux / amd64 시스템의 8K; 귀하의 시스템과 다를 수 있음)가 가득 찬 경우에만 플러시됩니다.

모든 유틸리티는 일반적인 규칙을 다음한다면, 당신은 출력이 당신의 모든 예에서 지연 볼 것입니다 ( cat|sed, cat|tr, 및 cat|tr|sed). 그러나 예외는 있습니다 : GNU cat는 출력을 버퍼링하지 않습니다. stdio기본 stdio버퍼링 정책을 사용하지 않거나 변경합니다 .

cat다른 유닉스 cat는 이런 식으로 동작하지 않기 때문에 GNU를 사용 하고 다른 유닉스 는 사용하지 않을 것입니다. 전통적인 유닉스 cat에는 -u버퍼되지 않은 출력을 요청 하는 옵션이 있습니다. GNU cat-u출력이 항상 버퍼링되지 않기 때문에 옵션을 무시합니다 .

따라서 catGNU 시스템에서 왼쪽에 파이프가 있으면 파이프를 통한 데이터 전달이 지연되지 않습니다. 은 cat심지어 라인으로 라인을 않을 것입니다 - 터미널이 그 일을한다. cat에 대한 입력을 입력하는 동안 터미널은 "표준"모드 라인 기반이며 백 스페이스 및 ctrl-U와 같은 편집 키를 사용하여 입력하기 전에 입력 한 행을 편집 할 수 있습니다 Enter.

cat|tr|sed예 에서는 을 누르 자마자 tr데이터를 계속 수신 하지만 기본 정책을 따르고 있습니다 . 출력이 파이프로 전달되므로 각 라인마다 플러시되지 않습니다. 버퍼가 가득 차거나 EOF가 수신 될 때 두 번째 파이프에 기록합니다.catEntertrstdio

sed또한 stdio기본 정책을 따르고 있지만 출력은 터미널로 보내 지므로 각 줄을 마치면 바로 작성합니다. 이것은 당신이 파이프 라인의 다른 쪽 끝까지 뭔가 쇼 전에 입력해야 어느 정도에 영향을 - 경우 sed였다 블록 버퍼링 출력을, 당신이 많은 (채우기 위해 두 번 입력해야 할 것 tr'출력 버퍼의 sed 의를 출력 완충기).

GNU sed에는 -u옵션이 있으므로 순서를 바꾸어 사용 cat|sed -u|tr하면 출력이 즉시 다시 나타납니다. ( sed -u옵션은 다른 곳에서 사용할 수 있지만 고대 유닉스 전통이라고 생각하지 않습니다 .)에 cat -u대한 동등한 옵션이 없다고 말할 수 있습니다 tr.

기본값 stdbuf을 사용하는 명령의 버퍼링 모드를 변경할 수 있는 유틸리티 가 있습니다 stdio. LD_PRELOADC 라이브러리가 지원하도록 설계되지 않은 것을 달성 하기 위해 사용 하기 때문에 약간 깨지기 쉽습니다 . 그러나이 경우에는 작동하는 것 같습니다.

cat | stdbuf -o 0 tr '[:lower:]' '[:upper:]' | sed 'p'

1
감사! 멋진 답변입니다. 아마도 질문에서 버퍼링을 언급해야하므로 찾을 수 있습니다.
xealits

tee그리고 dd또한 일반적으로 자신의 규칙에 의해 재생됩니다. 상상력있게 결합하면 세 가지 도구는 stdbuf배경 파이프 라인에 대한 요구를 상당히 무효화 할 수 있습니다 .
mikeserv

1
이것은 쓸모없는 고양이의 사용 을 피하는 이유 중 하나입니다 .
홉스

8

이것은 실제로 이해하고 더 많은 답변을 생각했습니다. 좋은 질문입니다 (다음에 찬성하겠습니다).

tr | sed위의 디버깅 항목 을 시도하지 않았습니다 .

>tr '[:lower:]' '[:upper:]' | sed 'p'
i am writing
still writing
now ctrl-d
I AM WRITING
I AM WRITING
STILL WRITING
STILL WRITING
NOW CTRL-D
NOW CTRL-D
>

분명히 tr버퍼링. 매일 새로운 것을 배우십시오!

편집 :

이것에 대해 생각할 때 우리는 원인을 찾아 냈지만 설명을 제공하지는 않았습니다. 당신이 경우 cat | tr당신이 있다면, 그것은 바로 쓰기 cat | sed, 그것은 바로 기록하지만 경우 tr | sed, 그것은 대기 를 위해 EOF. 나는 대답이에 묻혀 될 수 있습니다 제안 tr또는 sed다음 소스 코드 및 파이프 문제가되지.

편집 :

마지막 편집을 입력하는 동안 Wumpus 가 설명제공 한 것으로 보입니다 . 감사!


1
실제로 그들은 완충합니다! Wumpus가 언급했듯이 약 8kb 라인 테스트는 버퍼가 실제로 8Kb임을 보여줍니다. 나는 평판을 공유하기 위해 두 가지 대답을 모두 받아들이고 싶지만 Wumpus를보다 완전한 것으로 받아 들일 것입니다. 어쨌든 고마워!
xealits

1
문제는 없었습니다. 저의 경험적인 대답은 그의 지식 지식이었습니다.
Poisson Aerohead

stdbuf도움이 될 수있는 사용법을 보여주는이 질문도 참조하십시오 . unix.stackexchange.com/questions/182537/…
Joe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.