파이프, 파이프에서는 데이터가 어떻게 흐르나요?

파이프, 파이프에서는 데이터가 어떻게 흐르나요?

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

명령 파이프라인은 파일(텍스트, 문자열 배열)을 한 줄씩 처리하는 것 같습니다. (각 명령이 한 줄씩 작동하는 경우) 텍스트의 각 줄은 파이프되며 명령은 이전 줄이 전체 입력 처리를 마칠 때까지 기다리지 않습니다.

그러나 그것은 사실이 아닌 것 같습니다.

이것은 테스트 예시입니다. 몇 줄의 텍스트가 있습니다. 나는 그것들을 대문자로 바꾸고 각 줄을 두 번 반복합니다. 나는 이것을 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

그렇게되어야합니까? 왜 한줄씩 안되나요?

답변1

stdio대부분의 UNIX 프로그램에서 사용되는 C 표준 I/O 라이브러리( )는 공통 버퍼링 규칙을 따릅니다. 출력이 터미널로 이동하는 경우 각 줄 끝에서 플러시됩니다. 그렇지 않으면 버퍼(Linux/amd64 시스템에서는 8K, 사용자 시스템에서는 다를 수 있음)가 가득 찼을 때만 플러시됩니다.

모든 유틸리티에 대한 일반 규칙을 따르면 모든 예제( , 및 )에서 cat|sed출력 cat|tr지연을 볼 수 있습니다 cat|tr|sed. 한 가지 예외가 있습니다. GNU는 cat출력을 버퍼링하지 않습니다. 사용되지 않거나 stdio기본 stdio버퍼링 정책이 변경됩니다.

cat나는 당신이 다른 유닉스가 아닌 GNU를 사용하고 있다고 확신합니다 . cat왜냐하면 다른 유닉스에서는 이것을 하지 않기 때문입니다. 전통적인 유닉스에는 버퍼링되지 않은 출력을 요청하는 옵션이 cat있습니다 . -uGNU는 출력이 항상 버퍼링되지 않기 때문에 cat이 옵션을 무시합니다 .-u

cat따라서 왼쪽에 파이프가 있는 한 파이프를 통한 데이터 전송은 GNU 시스템에서 지연되지 않습니다. 한 줄씩 실행 하는 것도 아니고 cat터미널에서 수행하는 작업입니다. cat에 대한 입력을 입력하면 터미널은 백스페이스 및 Ctrl-U와 같은 편집 키를 사용하여 "표준" 모드인 행 기반으로 설정되어 send를 사용하기 전에 입력한 행을 편집할 수 있는 기회를 제공합니다 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'

답변2

실제로 이해하는 데 약간의 생각이 필요했고 답변하는 데 더 많은 시간이 걸렸습니다. 좋은 질문입니다(다음에 투표하겠습니다).

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

편집하다:

업스를 봤어요설명 제공마지막 편집 내용을 입력하는 중입니다. 감사해요!

관련 정보