다음 쉘 명령은 입력 스트림의 홀수 행만 인쇄해야합니다.
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)
나는 단순성을 위해 사용하지 않았다.
답변
헤드 유틸리티가 입력 스트림에서 요청한 것보다 많은 문자를 소비하는 것이 맞습니까?
예, 허용됩니다 (아래 참조).
유닉스 유틸리티에는 어떤 종류의 표준이 있습니까?
예, 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
답변
헤드 유틸리티는 지정된 시점에서 각 파일의 출력을 종료, 표준 출력에 입력 파일을 복사해야한다.
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'
답변
awk '{if (NR%2) == 1) print;}'