머리가 추가 문자를 먹습니다

머리가 추가 문자를 먹습니다

다음 쉘 명령은 입력 스트림의 홀수 행만 인쇄할 것으로 예상됩니다.

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예상대로 출력됩니다. 하지만 이는 다음에만 적용됩니다.핵심 도구이 유틸리티의 구현 head. 이것바쁜 상자구현은 여전히 ​​추가 문자를 소비하므로 출력은 12345.

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

--bytes옵션을 사용하는 경우에도 마찬가지입니다. 이 경우 읽어야 하는 바이트 수를 알 수 있습니다. 따라서 정확히 이 바이트 수만큼만 읽을 수 있습니다. 이것핵심 라이브러리구현에서는 이 기회를 활용하지만바쁜 상자그렇지 않은 경우에도 버퍼에서 필요한 것보다 더 많은 바이트를 읽습니다. 이는 아마도 구현을 단순화하기 위해 수행된 것 같습니다.

그래서 여기에 질문이 있습니다.head유틸리티가 입력 스트림에서 필요한 것보다 더 많은 문자를 올바르게 소비하고 있습니까? Unix 유틸리티에 대한 일종의 표준이 있습니까? 그렇다면 이 동작을 지정합니까?

폴리스티렌

Ctrl+C위 명령을 중지하려면 키를 눌러야 합니다 . Unix 유틸리티는 EOF.

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

나는 단순함을 위해 그것을 사용하지 않습니다.

답변1

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

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

Unix 유틸리티에 대한 일종의 표준이 있습니까?

예,POSIX 3권, 셸 및 유틸리티.

그렇다면 이 동작을 지정합니까?

실제로 소개에서:

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

head그 중 하나야표준 유틸리티, 따라서 POSIX 호환 구현에서는 위의 동작을 구현해야 합니다.

암소 비슷한 일종의 영양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)
...

read17바이트(사용 가능한 모든 입력)를 반환하고 head그 중 4개를 처리한 다음 13바이트 뒤로 이동하려고 시도하지만 그럴 수 없습니다. (GNU가 head8KiB 버퍼를 사용한다는 것도 여기서 볼 수 있습니다 .)

headCalculate the number of bytes (비표준)를 지시하면 읽을 바이트 수를 알고 있으므로 (이 방법으로 구현된 경우) 그에 따라 읽기를 제한할 수 있습니다. 이것이 테스트 가 작동하는 head -c 5이유 입니다. GNU는 head5바이트만 읽으므로 파일 설명자의 위치를 ​​복구하려고 할 필요가 없습니다.

문서를 파일에 작성하고 해당 파일을 사용하면 원하는 동작을 얻을 수 있습니다.

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

답변2

POSIX에서

이것머리유틸리티는 입력 파일을 표준 출력으로 복사하고 각 파일의 출력을 지정된 지점에서 종료해야 합니다.

head 입력에서 얼마나 읽어야 하는지는 알려주지 않습니다 . 바이트 단위로 읽도록 요청하는 것은 대부분의 경우 속도가 매우 느리기 때문에 어리석은 일입니다.

그러나 이것은 read내장/유틸리티에서 해결됩니다. read파이프에서 한 번에 한 바이트씩 모든 쉘을 찾을 수 있습니다.표준 텍스트이는 해당 행을 읽으려면 다음을 수행해야 하는 것으로 해석될 수 있습니다.

이것읽다유틸리티는 표준 입력에서 하나 이상의 쉘 변수로 단일 논리 행을 읽어야 합니다.

쉘 스크립트에서 사용하기 위한 read일반적인 사용 사례는 다음과 같습니다.

read someline
if something ; then 
    someprogram ...
fi

여기서 표준 입력은 someprogram셸의 표준 입력과 동일하지만 버퍼링된 읽기 이후에 남은 내용이 아닌 someprogram소비된 첫 번째 입력 행 이후의 모든 내용을 읽을 것으로 예상할 수 있습니다 . 반면에, 귀하의 예에서와 같이 사용하는 것은 훨씬 더 드뭅니다.readreadhead


정말로 다른 모든 행을 삭제하려면 전체 입력을 한 번에 처리할 수 있는 일부 도구를 사용하는 것이 더 좋고 더 빠릅니다.

$ 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

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

관련 정보