Відповіді:
Якщо ви хочете дотримуватися утиліти оболонки, ви можете 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
read -N
зупиняється на нульових байтах, тому це не підходящий спосіб роботи з бінарними даними. Взагалі, оболонки, крім zsh, не справляються з нулями.
Якщо ви хочете мати можливість працювати з двійковим файлом у оболонці, найкращий варіант (лише?) - це робота з інструментом 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 як довжина), а потім "рядок" як десяткове значення байтів:
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
ОНОВЛЕННЯ (заднім числом): ... Це питання / відповідь (моя відповідь) змушує мене думати про собаку, яка постійно переслідує машину .. Одного разу, нарешті, він підходить до машини .. Гаразд, він її спіймав, але він насправді не може з цим багато зробити ... Цей ансер 'ловить' рядки, але тоді ви не можете багато зробити з ними, якщо вони вбудували нульові байти ... (такий великий +1 відповіді Гіллу .. інша мова може бути тут порядком.)
dd
читає будь-які дані ... Це, звичайно, не пробиватиметься на нулі як "довжина" ... але якщо у вас є \ x00 де-небудь у ваших даних, вам потрібно буде бути творчим, як ви їх обробляєте; dd
не має проблем із цим, але ваш сценарій оболонки матиме проблеми (але це залежить від того, що ви хочете зробити з даними) ... Далі нижче в основному виводиться кожен "рядок даних" у файл із роздільником рядків між кожною строкою ...
btw: Ви говорите "персонаж", і я вважаю, що ви маєте на увазі "байт" ...
але слово "характер" стало неоднозначним в ці дні 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-байтний префікс на рядок ...
Префікс - це єдиний UTF-8 кодований символ Unicode ...
# 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
#
/dev/urandom
більшості одинаків. І випадкові дані тесту не є найкращими тестовими даними, ви повинні переконатися у вирішенні складних випадків, таких як, наприклад, нульові символи та новий рядок у граничних місцях.