bash 스크립트를 사용하여 이진 파일 내용을 읽는 방법은 무엇입니까?


15

문자를 읽은 다음 고정 길이의 문자열을 읽으려고합니다 (파일에서 문자열이 null로 끝나지 않고 길이는 앞의 문자로 표시됨).

bash 스크립트에서 어떻게 할 수 있습니까? 사후 처리를 할 수 있도록 문자열 변수를 정의하는 방법은 무엇입니까?

답변:


19

쉘 유틸리티를 사용 head하려면 많은 바이트를 추출 od하고 바이트를 숫자로 변환하는 데 사용할 수 있습니다.

export LC_ALL=C    # make sure we aren't in a multibyte locale
n=$(head -c 1 | od -An -t u1)
string=$(head -c $n)

그러나 이진 데이터 에는 작동하지 않습니다 . 두 가지 문제가 있습니다.

  • 명령 대체 는 명령 출력에서 마지막 개행$(…)제거 합니다. 상당히 쉬운 해결 방법이 있습니다. 출력이 개행 문자 이외의 문자로 끝나는 지 확인한 다음 해당 문자를 제거하십시오.

    string=$(head -c $n; echo .); string=${string%.}
  • 대부분의 쉘과 마찬가지로 Bash는 null 바이트 를 처리하는 데 좋지 않습니다 . bash 4.1부터는 명령 대체 결과에서 널 바이트가 단순히 삭제됩니다. 대시 0.5.5와 pdksh 5.2는 동일한 동작을 가지며 ATT ksh는 첫 번째 null 바이트에서 읽기를 중지합니다. 일반적으로 쉘과 유틸리티는 바이너리 파일을 다루는 데 적합하지 않습니다. Zsh는 예외이며 널 바이트를 지원하도록 설계되었습니다.

이진 데이터가있는 경우 Perl 또는 Python과 같은 언어로 전환해야합니다.

<input_file perl -e '
  read STDIN, $c, 1 or die $!;    # read length byte
  $n = read STDIN, $s, ord($c);   # read data
  die $! if !defined $n;
  die "Input file too short" if ($n != ord($c));
  # Process $s here
'
<input_file python -c '
  import sys
  n = ord(sys.stdin.read(1))      # read length byte
  s = sys.stdin.read(n)           # read data
  if len(s) < n: raise ValueError("input file too short")
  # Process s here
'

+1 쉘 스크립트가 항상 적절한 것은 아닙니다
forcefsck 22/40에

2
exec 3<binary.file     # open the file for reading on file descriptor 3
IFS=                   #
read -N1 -u3 char      # read 1 character into variable "char"

# to obtain the ordinal value of the char "char"
num=$(printf %s "$char" | od -An -vtu1 | sed 's/^[[:space:]]*//')

read -N$num -u3 str    # read "num" chars
exec 3<&-              # close fd 3

5
read -Nnull 바이트에서 중지되므로 이진 데이터로 작업하기에 적합하지 않습니다. 일반적으로 zsh 이외의 쉘은 널을 처리 할 수 ​​없습니다.
Gilles 'SO- 악마 중지'

2

쉘에서 바이너리 파일을 다룰 수있게하려면 가장 좋은 옵션은 hexdump 도구를 사용하는 것입니다.

hexdump -v -e '/1 "%u\n"' binary.file | while read c; do
  echo $c
done

X 바이트 만 읽습니다.

head -cX binary.file | hexdump -v -e '/1 "%u\n"' | while read c; do
  echo $c
done

길이를 읽고 (길이로 0으로 작업 한 후) 바이트 10 진수 값으로 "문자열"

len=$(head -c1 binary.file | hexdump -v -e '/1 "%u\n"')
if [ $len -gt 0 ]; then
  tail -c+2 binary.file | head -c$len | hexdump -v -e '/1 "%u\n"' | while read c; do
    echo $c
  done
fi

단지 많은 명령을 제시하는 대신, 그들이하는 일과 작동 방식을 설명 할 수 있습니까? 옵션은 무엇을 의미합니까? 사용자는 명령에서 어떤 결과를 기대할 수 있습니까? 의견에 응답하지 마십시오.  명확하고 완전하게 답변을 편집 하십시오.
G-Man, 'Reinstate

2
글쎄, 여기에 맨 페이지를 복사 할 수 있지만 요점은 보이지 않습니다. 여기에는 기본 명령 만 사용되며 유일한 트릭은 hexdump 사용입니다.
Clément Moulin-SimpleRezo

2
내 대답이 마음에 들지 않거나 이해하지 않기 때문에 다운 보팅?
Clément Moulin-SimpleRezo

1

업데이트 (후시와 함께) : ...이 질문 / 답변 (내 대답)으로 인해 차를 계속 쫓는 개를 생각하게됩니다. 어느 날, 마침내 차를 따라 잡습니다. 그는 정말로 그것으로 많은 것을 할 수 없습니다 ...이 anser는 문자열을 '잡아'하지만 null 바이트가 포함되어 있으면 많은 것을 할 수 없습니다 ... (Gills에 큰 +1 대답 .. 다른 언어가 여기에있을 수 있습니다.)

dd모든 데이터를 읽습니다 ... 확실히 "길이"로 0에서 짖는 소리는 없지만 ... 데이터에 \ x00이 있으면 창의력을 발휘 해야합니다. 를 처리하는 방법을 합니다. dd문제가 없지만 쉘 스크립트에 문제가 있습니다 (그러나 데이터로 수행하려는 작업에 따라 다름) ... 다음은 기본적으로 각 "데이터 문자열"을 각 스트레인 사이에 줄 구분선이있는 파일로 출력합니다 ...

btw : "문자"라고 말하고 " 바이트"를 의미 한다고 가정
하지만 유니 코드의 요즘에는 "문자"라는 단어가 모호 해졌습니다 .7 비트 ASCII 문자 세트 만 문자 당 단일 바이트를 사용합니다. ... 그리고 유니 코드 시스템에서도 바이트 수는 문자 인코딩 방법에 따라 다릅니다. . UTF-8, UTF-16 등

다음은 텍스트 "문자"와 바이트의 차이점을 강조하기위한 간단한 스크립트입니다.

STRING="௵"  
echo "CHAR count is: ${#STRING}"  
echo "BYTE count is: $(echo -n $STRING|wc -c)" 
# CHAR count is: 1
# BYTE count is: 3  # UTF-8 ecnoded (on my system)

당신의 길이 경우 문자가 1 바이트 길이이며 나타내는 바이트 길이를 ,이 스크립트는 ... 데이터가 유니 코드 문자가 포함 된 경우에도, 트릭을해야 dd만보고 바이트 에 관계없이 로케일 설정의 ...

이 스크립트는 dd이진 파일을 읽고 "===="구분자로 분리 된 문자열을 출력합니다 ... 테스트 데이터는 다음 스크립트를 참조하십시오

#   
div="================================="; echo $div
((skip=0)) # read bytes at this offset
while ( true ) ; do
  # Get the "length" byte
  ((count=1)) # count of bytes to read
  dd if=binfile bs=1 skip=$skip count=$count of=datalen 2>/dev/null
  (( $(<datalen wc -c) != count )) && { echo "INFO: End-Of-File" ; break ; }
  strlen=$((0x$(<datalen xxd -ps)))  # xxd is shipped as part of the 'vim-common' package
  #
  # Get the string
  ((count=strlen)) # count of bytes to read
  ((skip+=1))      # read bytes from and including this offset
  dd if=binfile bs=1 skip=$skip count=$count of=dataline 2>/dev/null
  ddgetct=$(<dataline wc -c)
  (( ddgetct != count )) && { echo "ERROR: Line data length ($ddgetct) is not as expected ($count) at offset ($skip)." ; break ; }
  echo -e "\n$div" >>dataline # add a newline for TEST PURPOSES ONLY...
  cat dataline
  #
  ((skip=skip+count))  # read bytes from and including this offset
done
#   
echo

출구

이 스크립트는 한 줄에 3 바이트 접두사가 포함 된 테스트 데이터를 빌드합니다
. 접두사는 단일 UTF-8로 인코딩 된 유니 코드 문자입니다.

# build test data
# ===============
  prefix="௵"   # prefix all non-zero length strings will this obvious 3-byte marker.
  prelen=$(echo -n $prefix|wc -c)
  printf \\0 > binfile  # force 1st string to be zero-length (to check zero-length logic) 
  ( lmax=3 # line max ... the last on is set to  255-length (to check  max-length logic)
    for ((i=1;i<=$lmax;i++)) ; do    # add prefixed random length lines 
      suflen=$(numrandom /0..$((255-prelen))/)  # random length string (min of 3 bytes)
      ((i==lmax)) && ((suflen=255-prelen))      # make last line full length (255) 
      strlen=$((prelen+suflen))
      printf \\$((($strlen/64)*100+$strlen%64/8*10+$strlen%8))"$prefix"
      for ((j=0;j<suflen;j++)) ; do
        byteval=$(numrandom /9,10,32..126/)  # output only printabls ASCII characters
        printf \\$((($byteval/64)*100+$byteval%64/8*10+$byteval%8))
      done
        # 'numrandom' is from package 'num-utils"
    done
  ) >>binfile
#

1
코드가 특히 무작위 테스트 데이터 생성기보다 복잡해 보입니다. /dev/urandom대부분의 유니스 에서 임의의 바이트를 얻을 수 있습니다 . 무작위 테스트 데이터는 최고의 테스트 데이터가 아니므로 경계 위치의 널 문자 및 줄 바꿈과 같은 어려운 경우를 처리해야합니다.
Gilles 'SO- 악마 중지'

네 고마워요 나는 / dev / random을 사용하려고 생각했지만 테스트 데이터 생성이 크게 중요하지 않다고 생각했으며 'numrandom'드라이브를 테스트하고 싶었다. 나는 당신의 대답을 면밀히 살펴 보았고 더 간결하다는 것을 제외하고는 당신이 거의 똑같은 일을하고 있음을 깨달았습니다. 나는 당신의 다른 언어 참조 에 초점을 두었습니다 . 그것을 작동시키는 것은 좋은 경험이었고, 이제 다른 언어에 대한 당신의 참조를 더 잘 이해합니다! \ x00은 쉘 스토퍼
Peter.O

0

이것은 단지 바이너리 파일을 복사합니다 :

 while read -n 1 byte ; do printf "%b" "$byte" ; done < "$input" > "$output"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.