BASH 스크립트의 stdin에서 EOF를 감지하는 방법은 무엇입니까?

BASH 스크립트의 stdin에서 EOF를 감지하는 방법은 무엇입니까?

내 스크립트에는 stdin에서 고정 크기 청크의 데이터를 읽고 추가 처리를 위해 이 데이터를 외부 프로그램에 한 번에 하나씩 보내야 하는 bash 함수가 있습니다. 함수 자체는 데이터가 있는 한 루프에서 실행되어야 하지만(입력은 항상 정수 덩어리로 보장됨) 데이터를 해석할 필요가 없으므로 함수의 표준 입력에서 EOF를 감지할 수 있는 방법을 원합니다. 처리할 데이터가 아직 남아 있는 경우 데이터를 소비합니다.

분명히 이를 수행하는 자연스러운 방법은 read다음과 같은 내장 함수를 사용하는 것입니다.

while read -r -n 0 ; do external_program ; done

옵션은 대부분의 개행 문자가 아닌 최대 이 바이트만 읽도록 -n지시 read하지만 불행히도 0바이트에서는 작동하지 않으므로 EOF에 대한 이상적인 테스트가 됩니다. 와 함께 작동 -n 1하지만 외부 프로그램으로 들어가는 스트림으로 "재생"되어야 하는 청크의 첫 번째 바이트를 소비합니다.

그렇다면 bash 내장 기능을 사용하는 것보다 더 좋은 방법이 있을까요?

답변1

실제로 0이 아닌 바이트 수를 읽으려고 시도하지 않고도 EOF를 감지할 수 있는지 확신할 수 없습니다.

read()시스템 호출에는 반환 값이 없기 때문입니다.분명히파일의 끝을 나타냅니다. 대신에 얻을 수 있는 것은 "읽은 바이트가 0이고 오류 없음"이며 이것이 무엇을 의미하는지 아는 것은 애플리케이션 코드에 달려 있습니다. 일반 파일에서는 파일 끝부터 읽거나 파일 끝을 넘어 읽을 때 데이터가 남아 있지 않을 때 분명히 이런 일이 발생합니다.

그러나 터미널에서는 사용자가 빈 줄에서 ^D를 눌러 터미널 인터페이스가 해당 지점의 콘텐츠를 반환하기 때문에 이런 일이 발생할 수 있습니다. 이는 데이터그램 소켓에서는 아무것도 아니며 길이가 0인 정보를 보내고 받는 것이 가능합니다. . 이러한 경우 중 어느 것도 실제 종료를 나타내지 않습니다. 터미널은 ^D 이후의 데이터를 읽을 수 있으며 소켓은 길이가 0인 메시지 이후에 추가 메시지를 받을 수 있습니다. (일반 파일에서도 파일에 동시에 다른 프로세스가 연결되어 있으면 후속 파일에서 데이터가 반환될 수 있습니다. EOF를 반복해서 읽는 것이 가장 간단한 구현입니다 tail -f.)

0바이트를 읽도록 명시적으로 요청하면 EOF 상태인지 여부에 관계없이 0바이트(또는 오류)가 발생합니다.


외부 프로그램이 큰 문제 없이 EOF를 처리할 수 있다면 이를 나타내는 종료 코드를 반환하는 것만으로도 최상의 결과를 얻을 수 있을 것입니다. 그러면 다음과 같이 할 수 있습니다:

while external_program; do
    # do we need to do anything here but loop?
    true 
done

또는 운이 좋다면 다른 EOF 종료 상태를 얻을 수도 있습니다.

while true; do
    external_program
    ret=$?
    if [ "$ret" = 0 ]; then
        echo "ok, continue"
    elif [ "$ret" = 1 ]; then
        echo "deal with this error"
        # but what now?
    elif [ "$ret" = 2 ]; then
        echo "got EOF, stopping"
        break
    fi
done

프로그램이 수신한 입력의 유효성을 검사해야 하기 때문에 EOF를 처리하도록 하는 것이 합리적입니다.

그렇게 할 수 없다면 Bash가 데이터 덩어리를 읽고 이를 프로그램에 전달하도록 할 수 있습니다(실제로 충분한 데이터를 읽었다면).

blocksize=123
while IFS= read -d '' -r -n "$blocksize" data && [ "${#data}" = "$blocksize"]; do
    printf "%s" "$data" | externalprogram
done

그러나 이는 데이터에 NUL 바이트( \0)가 포함되지 않은 경우에만 Bash에서 작동합니다. 그렇다면 Zsh(또는 일부 실제 프로그래밍 언어)로 전환하거나 유사한 head -c "$blocksize" > tmpfile.

관련 정보