파이프에서 읽을 때 왜 'sed q'가 다르게 작동합니까?


25

다음을 포함하는 'test'라는 테스트 파일을 만들었습니다.

xxx
yyy
zzz

나는 명령을 실행했다 :

(sed '/y/ q'; echo aaa; cat) < test

그리고 나는 얻었다 :

xxx
yyy
aaa
zzz

그런 다음 나는 달렸다.

cat test | (sed '/y/ q'; echo aaa; cat)

그리고 얻었다 :

xxx
yyy
aaa

의문

sed'y'가있는 줄이 나타날 때까지 읽고 인쇄 한 다음 중지합니다. 첫 번째 경우 (두 번째는 아님), 고양이는 나머지를 읽고 인쇄합니다.

누군가이 행동의 차이 뒤에 어떤 현상이 있는지 설명 할 수 있습니까?

또한 Ubuntu 16.04 및 Centos 6에서는이 방식으로 작동하지만 Centos 7에서는 명령이 'zzz'를 인쇄하지 않습니다.


catstdin이 실제 파일에 바인딩되어 있기 때문에 (서브 셸에서) 첫 번째 경우 파일 설명자를 다시 사용할 수 있다고 생각 합니다. 두 번째 경우 stdin은 실제 파일이 아닌 파이프에서 온 것입니다. 또한 (sed '/y/ q'; echo aaa; cat) < <(cat test)인쇄되지 않습니다 zzz.
— Martin Nyolt

1
더 간단한 예 : (head -n1; head -n1) < test와cat test | (head -n1; head -n1)
— 마틴 Nyolt

답변:


22

입력 파일 인 경우 시크 하거나 (일반 파일의 읽기와 같은) 되지 않은 시크 (파이프에서 읽기 등) sed(및 기타 표준 유틸리티) 다른 (읽기 동작합니다 INPUT FILES섹션 이 링크를 ).

문서에서 인용 :

표준 유틸리티가 탐색 가능한 입력 파일을 읽고 파일 끝에 도달하기 전에 오류없이 종료되는 경우, 유틸리티는 열린 파일 설명의 파일 오프셋이 유틸리티가 처리 한 마지막 바이트 바로 위에 위치하는지 확인해야합니다.

그래서 :

(sed '/y/ q'; echo aaa; cat) < test

sedqEOF에 도달하기 전에 uit 명령을 수행 하여 zzz줄의 시작 부분에서 파일 오프셋을 남겨 두어 나머지 줄 cat을 계속 인쇄 할 수 있습니다 (GNU sed는 어떤 조건에서 POSIX 호환이 아닙니다 (아래 참조)).

그리고 문서에서 계속 :

찾을 수없는 파일의 경우 해당 파일에 대한 열린 파일 설명의 파일 오프셋 상태는 지정되지 않습니다.

이 경우 동작은 지정되지 않습니다. 대부분의 표준 도구, 포함 sed은 가능한 한 많이 입력을 소비합니다. 파일 오프셋을 복원하지 않고 yyy행을 읽습니다 . q그래서 아무것도 남지 않습니다 cat.


GNU sed는 표준을 준수하지 않으며 시스템의 stdio 구현 및 glibc 버전에 따라 다릅니다.

$ (gsed '/y/ q'; echo aaa; cat) < test
xxx
yyy
aaa

여기서 결과는 Mac OSX 10.11.6, 가상 머신 Centos 7.2-glibc 2.17, Ubuntu 14.04-glibc 2.19에서 얻은 것으로, CEPH 백엔드를 사용하여 Openstack에서 실행됩니다.

이러한 시스템에서 -u옵션을 사용 하여 표준 동작을 달성 할 수 있습니다 .

(gsed -u '/y/ q'; echo aaa; cat) </tmp/test

그리고 파이프 :

$ cat test | (gsed -u '/y/ q'; echo aaa; cat)
xxx
yyy
aaa
zzz

한 sed번에 1 바이트를 읽어야 하기 때문에 성능이 크게 비효율적 입니다. 의 부분 출력 strace:

$ strace -fe read sh -c '{ sed -u "/y/q"; echo aaa; cat; } <test'
...
[pid  5248] read(3, "", 4096)           = 0
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "\n", 1)            = 1
xxx
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "\n", 1)            = 1
yyy
...

1
GNU sed의 경우 시스템의 stdio 구현에 따라 다릅니다. GNU 시스템 (GNU libc 사용)에서, GNU sed는 exit()stdio가 관리하는 파일을 찾는 것처럼 호환됩니다 .
— Stéphane Chazelas

@ StéphaneChazelas : 확인하는 방법? Centos 7.2와 Ubuntu 14.04 VM sed은 호환되지 않으며 manjaro 랩탑도 호환됩니다. 모두 동일한 sed 버전 4.2.2
— cuonglm

@ StephaneChazelas : 후드 아래에서 일어난 것처럼 들립니다. 내 가상 머신에서 수행 strace -f sh -c '{ sed "/y/q"; echo aaa; cat; } <test'되지 않았 음 lseek()을 표시하고 내 manjaro에서 a lseek()가 이전에 호출되었습니다 exit_group().
— cuonglm

나는 그것이 GNU libc의 버전에 있다고 생각합니다. main() { char buf[999]; gets(buf); }'프로그램으로 테스트 할 수 있습니다 .
— Stéphane Chazelas

1
@ StéphaneChazelas : 확인했습니다. 내 VM 모두 2.17 및 2.19를 가지고 있지만 manjaro의 VM은 2.23입니다. 이것은 glibc 버그를 고려합니까? glibc 버전 사이의 변경 사항에 대한 정보가
— 있습니까
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.