그룹화 명령의 POSIX 지정 꼬리 동작입니까?

그룹화 명령의 POSIX 지정 꼬리 동작입니까?

tail다른 표준 도구와 함께 사용그룹화 명령일부 강력한 건물을 지을 수 있습니다. 예를 들어, 파일의 첫 번째 줄과 마지막 줄을 얻으려면 다음을 수행하세요.

$ seq 10 > file
$ { head -n1; tail -n1; } <file
1
10

파이프에서 그룹 명령으로 파일 내용을 공급할 때 tail파이프가 다음과 같기 때문에 출력을 생성할 수 없습니다.연합 국가-찾다유능한:

$ seq 10 | { head -n1; tail -n1; }
1

이제 콘텐츠가 충분히 크면 tail다음과 같이 작동합니다.

$ seq 10000 | { head -n1; tail -n1; }
1
10000

lseek첫 번째 실패 후에 tail는 실패가 아니라는 걸 알기 때문이다.검색 가능파일 디스크립터이며 파이프의 내용을 아직 모두 읽지 않았으므로 끝까지 내용을 읽기 시작합니다.

사용자의 관점에서는 입력 크기에 관계없이 동작이 일관되기를 원합니다. POSIX 문서를 살펴봤지만 tail설명 lseek을 찾지 못했습니다.

이 동작은 POSIX에서 지정합니까? 그렇지 않다면 어떻게 결과를 일관되게 만들 수 있습니까?


저는 GNU tail과 FreeBSD tail로 테스트했는데 둘 다 동일한 동작을 보입니다.

답변1

문제는 여기에 있지 않지만 tail여기서는 head출력하려는 ​​첫 번째 줄보다 파이프에서 더 많은 내용을 읽습니다(따라서 읽을 내용이 없음 tail).

예, POSIX와 호환됩니다.

head입력을 검색할 수 있는 경우 커서는 출력의 마지막 줄 바로 뒤 stdin에 유지되어야 하지만 그렇지 않은 경우에는 그렇지 않습니다.

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap01.html:

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

head검색할 수 없는 파일로 이 작업을 수행할 수 있다는 것은 한 번에 1바이트를 읽어야 한다는 것을 의미하므로 매우 비효율적입니다 . 이것이 유틸리티나 GNU가 read이 옵션을 사용하여 수행하는 작업입니다.linesed-u

따라서 이 동작을 원하면 head -n 20로 바꿀 수 있습니다.gsed -u 20q

여기서는 다음을 원할 것입니다.

sed -e 1b -e '$b' -e d

대신에. 여기서는 하나의 도구 호출만 있으므로 두 도구 호출 간에 내부 버퍼를 공유할 수 없는 문제가 없습니다. 그러나 대용량 파일의 경우 sed전체 파일을 읽는 것이 덜 효율적이며 검색 가능한 파일의 경우 tail대부분의 파일을 건너뜁니다.구하다파일 끝 부분에 가깝습니다.

버퍼링에 대한 관련 토론을 참조하세요.쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?.

tail스트림의 꼬리 는 표준 입력으로 출력되어야 합니다. 최적화 및 검색 가능한 파일로서 구현 시 파일 끝을 탐색하여 거기에서 후행 데이터를 얻을 수 있지만 호출 시 초기 위치 이전 지점으로 다시 탐색하는 것을 허용하지 않습니다 tail(Busybox는 tail이전에 이 버그가 있습니다).

예를 들어:

{ cat; tail -n 1; } < file

tail마지막 행으로 돌아갈 수는 있지만 그렇지 file않습니다. 표준 입력은 cat파일 끝에 커서가 있는 빈 스트림입니다 . 파일을 더 자세히 살펴봄으로써 이 스트림에서 데이터를 복구할 수 없습니다.

(위의 텍스트는 확인을 위해 줄을 그었습니다.공개 그룹의 설명여러 구현이 올바르게 수행되지 않았다는 점을 고려하십시오)


1 소켓(탐색할 수 없는 파일)에 대한 head내장 기능( ksh93앞에 놓으면 활성화됨)/opt/ast/bin$PATH나타나다recvfrom(..., MSG_PEEK)실제 입력 전(사용)읽다너무 많이 읽지 않도록 하려면 얼마나 읽어야 하는지 확인하세요. 다른 파일 형식의 경우 한 번에 1바이트씩 읽는 것으로 대체됩니다. 이것은 약간 더 효율적입니다. 이것이 socketpair()Pipes를 구현하는 대신 s를 사용하는 주된 이유 라고 생각합니다 pipe(). 다른 프로세스가 소켓 사이에서 데이터를 읽는 경우 경쟁 조건이 발생할 수 있으므로 이것이 완전히 안전한 것은 아닙니다.나타나다그리고읽다.

관련 정보