파이프에서 읽을 때 왜 '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) < testcat 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 sedexit()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.