BASH 내장 읽기를 사용하여 0바이트를 포함한 이진 데이터를 읽는 방법은 무엇입니까?

BASH 내장 읽기를 사용하여 0바이트를 포함한 이진 데이터를 읽는 방법은 무엇입니까?

나는 read buildin 명령을 사용하여 읽을 때 bash가 입력 시 이진 0을 무시한다는 것을 발견했습니다. 이 문제를 해결할 방법이 있나요?

작업은 한 번에 12바이트 바이너리 블록, 즉 16비트 정수 2개와 32비트 정수 2개를 전송하는 파이프에서 데이터를 읽는 것입니다. 데이터 속도는 낮으며 성능에는 문제가 없습니다. bash 변수는 C 스타일이기 때문에 분명히 read -N 12 struct효과가 없으며 NUL 이상의 바이트에는 액세스할 수 없습니다. 그래서 바이트 단위로 데이터를 읽어야 할 것 같습니다 read -N 1 byte. 해결하기 쉬운 문제는 이스케이프(필수 -r) 및 UTF 멀티바이트 문자 인코딩( export LC_ALL=C)입니다. 지금까지 해결할 수 없는 문제는 0바이트를 처리하는 것입니다. 나는 그들이 빈 변수로 나타날 것이라고 생각했지만 byte실제로 read -r -N 1 byte0에서는 전혀 반환하지 않고(0을 무시하고) 대신 데이터 스트림에서 0이 아닌 다음 바이트를 반환합니다.

이것이 제가 하려는 작업이며 0이 입력되지 않는 한 완벽하게 작동합니다.

export LC_ALL=C

while true;
  do
     for ((index = 0; index < 12; index++))
       do
          read -r -N 1 byte
          if [ -n "${byte}" ]; then
               struct[${index}]=$(echo -n "${byte}" | od -An -td1)
             else
               struct[${index}]=0
            fi
       done
... # some arithmetics reconstructing the four bitfields and processing them
  done < pipe

else의 분기는 결코 사용되지 않는 것으로 나타났습니다 if. 0을 포함하는 12바이트 데이터 블록으로 인해 루프가 for12번 실행되지는 않지만 대신 더 많은 데이터가 배열을 채울 때까지 기다립니다 struct. 다음 명령을 사용하여 파이프에 12바이트를 공급하여 이 동작을 보여줍니다.

echo -en "ABCDE\tGH\0JKL" > pipe

자신을 속이기 쉽기 때문에 0을 보내는 것을 확인했습니다.

~# mkfifo pipe
~# od -An -td1 <pipe &
[1] 25512
~# echo -en "ABCDE\tGH\0JKL" > pipe
~#    65   66   67   68   69    9   71   72    0   74   75   76

[1]+  Done                    od -An -td1 < /root/pipe

bash의 이러한 동작을 변경할 수 있는 방법이 있습니까? 아니면 0바이트를 어떻게 읽을 수 있나요?

답변1

bash변수는 NUL 바이트를 저장할 수 없습니다( zshNUL 바이트만 저장할 수 있지만 ksh93's 도 참조 printf %B하고 typeset -bbase64 인코딩을 사용하세요). 내장 함수 는 read입력에서 NUL 바이트도 건너뜁니다.

그러나 여기서는 다음을 사용할 수 있습니다.

LC_ALL=C IFS= read -rd '' -n1 c

즉, NUL로 구분된 레코드에서 최대 1바이트를 읽습니다. 따라서 비어 있으면 EOF(그러나 종료 상태는 0이 아님) 또는 NUL 바이트를 읽었음을 $c의미합니다 .read

두 경우 모두 다음을 사용하여 바이트의 숫자 값을 얻을 수 있습니다.

LC_ALL=C printf -v value %d "'$c"

그래서:

while
  IFS= LC_ALL=C read -rd '' -n1 c &&
    LC_ALL=C printf -v value %d "'$c"
do
  echo "Got byte with value $value"
done

EOF까지 한 번에 한 바이트씩 읽고 NUL 바이트를 지원합니다.

아니면 이렇게 할 수도 있습니다:

value=$(dd bs=1 count=1 2> /dev/null | od -An -vtu1)

또는 일부 od구현을 통해:

value=$(od -N1 -An -vtu1)

icanon이는 추가 프로세스를 분기하고 외부 실행 파일을 실행하는 것을 의미하지만(stdin이 터미널 장치인 경우 모드 에서 벗어나지 않습니다 read).

관련 정보