Как использовать скрипт bash для чтения содержимого двоичного файла?

Я хочу прочитать символ, а затем фиксированную длину строки (строка не завершена нулем в файле, а ее длина указана предыдущим символом).

Как я могу сделать это в сценарии bash? Как определить строковую переменную, чтобы я мог выполнить некоторую пост-обработку?

  • Grep: поиск и замена полной строки
  • Моя awk-программа для изменения пробелов в вкладках не работает
  • Каков наиболее подходящий способ анализа значений из этого вывода?
  • Переформатирование таблицы с помощью awk
  • Создать таблицу с частотой уникальных имен, полученных из нескольких CSV-файлов
  • Как редактировать файлы на месте при выполнении операции над значениями, соответствующими найденному шаблону?
  • Вставьте новую строку после разбитой последовательности чисел awk / unix / shell scripting
  • Как найти и переместить текстовую строку внутри файла?
  • 4 Solutions collect form web for “Как использовать скрипт bash для чтения содержимого двоичного файла?”

    Если вы хотите использовать утилиты shell, вы можете использовать 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, как и большинство оболочек, плохо справляется с нулевыми байтами . Начиная с bash 4.1, пустые байты просто удаляются из результата подстановки команды. Dash 0.5.5 и pdksh 5.2 имеют такое же поведение, и ATT ksh перестает читать первый байт. В общем, оболочки и их утилиты не предназначены для работы с бинарными файлами. (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 ' 
     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 

    ОБНОВЛЕНИЕ (с ретроспективностью): … Этот вопрос / ответ (мой ответ) заставляет меня думать о собаке, которая продолжает гоняться за машиной. Однажды, наконец, он догоняет машину. Ладно, он поймал ее, но он действительно не может много сделать с этим … Этот anser «ловит» строки, но тогда вы не можете много сделать с ними, если у них есть встроенные нуль-байты … (так что большой ответ +1 Жилю .. здесь может быть другой язык.)

    dd читает все и все данные … Это, конечно, не будет baulk в нуле как «длина» … но если у вас есть \ x00 в любом месте ваших данных, вам нужно будет проявлять творческий подход к тому, как вы его обрабатываете; dd имеет никаких проблем с ним, но ваш скрипт оболочки будет иметь проблемы (но это зависит от того, что вы хотите делать с данными) … Ниже в основном выводится каждая «строка данных», в файл с разделителем строк между каждым Strin …

    Кстати: вы говорите «персонаж», и я предполагаю, что вы имеете в виду «байт» …
    но слово «символ» стало неоднозначным в эти дни UNICODE, где только 7-битный набор символов ASCII использует один байт на символ … И даже в системе Unicode количество байтов варьируется в зависимости от метода кодирования символы , например. 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 байт и указывает длину байта , то этот скрипт должен сделать трюк, даже если данные содержат символы Unicode … 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-байтовый префикс в строке …
    Префикс – это единственный кодированный Unicode символ 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 # 

    Этот экземпляр просто копирует двоичный файл:

      while read -n 1 byte ; do printf "%b" "$byte" ; done < "$input" > "$output" 
    Linux и Unix - лучшая ОС в мире.