머리는 여분의 캐릭터를 먹는다


15

다음 쉘 명령은 입력 스트림의 홀수 행만 인쇄해야합니다.

echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)

그러나 대신 첫 번째 줄을 인쇄합니다 aaa.

-c( --bytes) 옵션 과 함께 사용하면 마찬가지입니다 .

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)

이 명령 1234512345은 예상대로 출력 됩니다. 그러나 이것은 유틸리티 의 coreutils 구현 에서만 작동 head합니다. 비지 박스의 출력이 단지 그래서 구현은 여전히 추가 문자를 먹는다 12345.

이 특정 구현 방법은 최적화 목적으로 수행 된 것 같습니다. 줄이 끝나는 곳을 알 수 없으므로 읽을 문자 수를 알 수 없습니다. 입력 스트림에서 추가 문자를 사용하지 않는 유일한 방법은 스트림을 바이트 단위로 읽는 것입니다. 그러나 한 번에 한 바이트 씩 스트림에서 읽는 것이 느려질 수 있습니다. 따라서 head입력 스트림을 충분히 큰 버퍼로 읽은 다음 해당 버퍼의 행을 계산합니다.

--bytes옵션을 사용 하는 경우에도 마찬가지 입니다. 이 경우 읽을 바이트 수를 알고 있습니다. 따라서이 바이트 수만큼 정확하게 읽을 수 있습니다. corelibs의 구현은이 기회를 사용하지만, 비지 박스의 하나하지, 그것은 여전히 버퍼에 필요한 것보다 더 많은 바이트를 읽어 않습니다. 아마도 구현을 단순화하기 위해 수행되었을 것입니다.

그래서 질문입니다. head유틸리티가 요청한 것보다 많은 문자를 입력 스트림에서 소비 하는 것이 맞 습니까? 유닉스 유틸리티에는 어떤 종류의 표준이 있습니까? 그리고 있다면,이 동작을 지정합니까?

추신

Ctrl+C위의 명령을 중지하려면 을 눌러야 합니다. 유닉스 유틸리티는 그 이상을 읽는 데 실패하지 않습니다 EOF. 누르기를 원하지 않으면 더 복잡한 명령을 사용할 수 있습니다.

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)

나는 단순성을 위해 사용하지 않았다.


2
neardupe unix.stackexchange.com/questions/48777/…unix.stackexchange.com/questions/84011/… . 또한,이 타이틀이 영화에 있었다면 SXZardoz 일 것 입니다 :)
dave_thompson_085

답변:


30

헤드 유틸리티가 입력 스트림에서 요청한 것보다 많은 문자를 소비하는 것이 맞습니까?

예, 허용됩니다 (아래 참조).

유닉스 유틸리티에는 어떤 종류의 표준이 있습니까?

예, POSIX 3 권, Shell & Utilities .

그리고 있다면,이 동작을 지정합니까?

소개에서 :

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

head표준 유틸리티 중 하나 이므로 POSIX 호환 구현은 위에서 설명한 동작을 구현해야합니다.

GNU head 파일 디스크립터를 올바른 위치에 두려고 시도하지만 파이프를 찾는 것은 불가능하므로 테스트에서 위치를 복원하지 못합니다. 당신은 이것을 사용하여 볼 수 있습니다 strace:

$ echo -e "aaa\nbbb\nccc\nddd\n" | strace head -n 1
...
read(0, "aaa\nbbb\nccc\nddd\n\n", 8192) = 17
lseek(0, -13, SEEK_CUR)                 = -1 ESPIPE (Illegal seek)
...

read반환 17 바이트 (사용 가능한 모든 입력), head그 중 네 가지를 처리하고 다시 13 바이트를 이동하려고하지만 할 수 없습니다. (여기서 GNU head가 8 KiB 버퍼를 사용 한다는 것을 알 수 있습니다 .)

당신이 말할 때 head(비표준 인) 바이트를 계산하는 바이트가 읽는 방법, 그것을 알고 그래서 할 수 그에 따라 읽기를 제한 (방법 있음을 구현할 경우). 이것이 head -c 5테스트가 작동하는 이유입니다 . GNU head는 5 바이트 만 읽으므로 파일 디스크립터의 위치를 ​​복원 할 필요가 없습니다.

문서를 파일에 쓰고 대신 사용하면 다음과 같은 동작이 나타납니다.

$ echo -e "aaa\nbbb\nccc\nddd\n" > file
$ < file (while true; do head -n 1; head -n 1 >/dev/null; done)
aaa
ccc

2
하나는 사용할 수 있습니다 line(현재 POSIX / XPG에서 제거하지만, 많은 시스템에서 여전히 사용 가능) 또는 read( IFS= read -r line) 문제를 방지하기 위해 한 번에 한 바이트를 읽을 대신 유틸리티.
Stéphane Chazelas

3
head -c 55 바이트를 읽을 지 전체 버퍼를 읽을 지 여부 는 구현에 따라 다르며 ( head -c표준이 아님 에도 유의)이를 신뢰할 수는 없습니다. dd bs=1 count=55 바이트를 초과하지 않도록 보장 해야 합니다.
Stéphane Chazelas

@ Stéphane에게 감사합니다 -c 5. 설명을 업데이트했습니다 .
Stephen Kitt

점을 유의 head의 내장은 ksh93한 번에 하나의 바이트를 판독 head -n 1입력 시크가 아닌 경우.
Stéphane Chazelas

1
@anton_rh dd는 파이프에서 읽기 bs=1를 사용하면 count요청보다 적은 값을 반환 할 수 있지만 eof에 도달하지 않으면 1 바이트 이상 으로 파이프와 함께 올바르게 작동합니다 . GNU ddiflag=fullblock이를 완화 할 수 있습니다.
Stéphane Chazelas

6

POSIX에서

헤드 유틸리티는 지정된 시점에서 각 파일의 출력을 종료, 표준 출력에 입력 파일을 복사해야한다.

head 입력에서 읽어야 하는 양에 대해서는 아무 것도 말하지 않습니다 . 대부분의 경우 속도가 매우 느리기 때문에 바이트 단위로 읽도록 요구하는 것은 어리석은 일입니다.

그러나 이것은 read내장 / 유틸리티로 해결됩니다. read파이프에서 한 번에 한 바이트 씩 찾을 수있는 모든 쉘 과 표준 텍스트 를 해석하여 한 줄만 읽을 수 있도록 해석해야합니다.

판독 유틸리티는 하나 개 이상의 쉘 변수로 표준 입력 단일 논리 라인을 판독한다.

의 경우 read쉘 스크립트에서 사용되는, 일반적인 사용 사례는 다음과 같이 될 것이다 :

read someline
if something ; then 
    someprogram ...
fi

여기서의 표준 입력은 someprogram쉘 의 표준 입력과 동일하지만에 someprogram의해 read버퍼링 된 읽기 후에 남은 것이 아니라에 의해 소비 된 첫 번째 입력 라인 다음에 오는 모든 것을 읽을 수 있습니다 read. 반면에, head예와 같이 사용 하는 것이 훨씬 더 드문 경우입니다.


다른 줄을 모두 삭제하려면 전체 입력을 한 번에 처리 할 수있는 도구를 사용하는 것이 좋습니다 (예 : 더 빠름).

$ seq 1 10 | sed -ne '1~2p'   # GNU sed
$ seq 1 10 | sed -e 'n;d'     # works in GNU sed and the BSD sed on macOS

$ seq 1 10 | awk 'NR % 2' 
$ seq 1 10 | perl -ne 'print if $. % 2'

그러나의 "입력 파일"섹션 참조 볼륨 3에 POSIX 소개를 ...
스티븐 키트를

1
POSIX의 말 : "표준 유틸리티가 탐색 가능한 입력 파일을 읽고 파일 끝에 도달하기 전에 오류없이 종료 될 때 유틸리티는 열린 파일 설명의 파일 오프셋이 마지막으로 처리 된 바이트 바로 다음에 올바르게 위치하도록해야합니다. 검색 할 수없는 파일의 경우 해당 파일에 대한 열린 파일 설명의 파일 오프셋 상태는 지정되지 않습니다. "
AlexP

2
당신이 사용하지 않는 것을 참고 -r, read(없이 한 줄 이상을 읽을 수 있습니다 IFS=그것은 또한 선행 및 공백과 탭을 후행 (기본 값으로 제거 할 $IFS)).
Stéphane Chazelas

@AlexP, 예, Stephen은 그 부분을 연결했습니다.
ilkkachu

점을 유의 head의 내장은 ksh93한 번에 하나의 바이트를 판독 head -n 1입력 시크가 아닌 경우.
Stéphane Chazelas

1
awk '{if (NR%2) == 1) print;}'

Hellóka :-) 사이트에 오신 것을 환영합니다! 보다 정교한 답변을 선호합니다. 그들은 미래의 구글 사람들에게 유용해야합니다.
peterh-Reinstate Monica
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.