Ответ 1
Новое сообщение в сентябре 2016 года!
Поскольку это очень специфично, это дополнение будет представлено на самом дне этого.
Обновление для добавления конкретной версии bash (с базинизмами)
С новой версией printf
вы могли бы сделать многое, не имея fork ($(...)
), чтобы ваш script был намного быстрее.
Сначала давайте посмотрим (используя seq
и sed
), как разобрать вывод hd:
echo ;sed <(seq -f %02g 0 $[COLUMNS-1]) -ne '
/0$/{s/^\(.*\)0$/\o0337\o033[A\1\o03380/;H;};
/[1-9]$/{s/^.*\(.\)/\1/;H};
${x;s/\n//g;p}';hd < <(echo Hello good world!)
0 1 2 3 4 5 6 7
012345678901234567890123456789012345678901234567890123456789012345678901234567
00000000 48 65 6c 6c 6f 20 67 6f 6f 64 20 77 6f 72 6c 64 |Hello good world|
00000010 21 0a |!.|
00000012
Если шестнадцатеричная часть начинается с col 10 и заканчивается на col 56, на расстоянии 3 символа и имеет дополнительное пространство в столбце 34.
Таким образом, синтаксический анализ может быть выполнен следующим образом:
while read line ;do
for x in ${line:10:48};do
printf -v x \\%o 0x$x
printf $x
done
done < <( ls -l --color | hd )
Старый оригинальный пост
Изменить 2 для шестнадцатеричного, вы можете использовать hd
echo Hello world | hd
00000000 48 65 6c 6c 6f 20 77 6f 72 6c 64 0a |Hello world.|
или od
echo Hello world | od -t x1 -t c
0000000 48 65 6c 6c 6f 20 77 6f 72 6c 64 0a
H e l l o w o r l d \n
в ближайшее время
while IFS= read -r -n1 car;do [ "$car" ] && echo -n "$car" || echo ; done
попробуйте:
while IFS= read -rn1 c;do [ "$c" ]&&echo -n "$c"||echo;done < <(ls -l --color)
Объясняю:
while IFS= read -rn1 car # unset InputFieldSeparator so read every chars
do [ "$car" ] && # Test if there is ``something''?
echo -n "$car" || # then echo them
echo # Else, there is an end-of-line, so print one
done
Edit; Вопрос был отредактирован: нужны шестнадцатеричные значения!?
od -An -t x1 | while read line;do for char in $line;do echo $char;done ;done
Демо:
od -An -t x1 < <(ls -l --color ) | # Translate binary to 1 byte hex
while read line;do # Read line of HEX pairs
for char in $line;do # For each pair
printf "\x$char" # Print translate HEX to binary
done
done
Демо 2: У нас есть как hex, так и двоичный
od -An -t x1 < <(ls -l --color ) | # Translate binary to 1 byte hex
while read line;do # Read line of HEX pairs
for char in $line;do # For each pair
bin="$(printf "\x$char")" # translate HEX to binary
dec=$(printf "%d" 0x$char) # translate to decimal
[ $dec -lt 32 ] || # if caracter not printable
( [ $dec -gt 128 ] && # change bin to a single dot.
[ $dec -lt 160 ] ) && bin="."
str="$str$bin"
echo -n $char \ # Print HEX value and a space
((i++)) # count printed values
if [ $i -gt 15 ] ;then
i=0
echo " - $str"
str=""
fi
done
done
Новое сообщение в сентябре 2016 года:
Это может быть полезно в очень конкретных случаях (я использовал их для ручного копирования разделов GPT между двумя дисками, на низком уровне, без установки /usr
...)
Да, bash может читать двоичные файлы!
... но только один байт, на один... (потому что ` char (0) 'не может быть правильно прочитан, единственный способ правильно их прочитать - рассмотреть конец файла, где если никакой символ не читается, а конец файла не достигнут, тогда чтение символа - char (0)).
Это скорее доказательство концепции, чем инструмент relly usefull: существует чистый bash версия hd
(hexdump).
Это использование последних базизмов в bash v4.3
или выше.
#!/bin/bash
printf -v ascii \\%o {32..126}
printf -v ascii "$ascii"
printf -v cntrl %-20sE abtnvfr
values=()
todisplay=
address=0
printf -v fmt8 %8s
fmt8=${fmt8// / %02x}
while LANG=C IFS= read -r -d '' -n 1 char ;do
if [ "$char" ] ;then
printf -v char "%q" "$char"
((${#char}==1)) && todisplay+=$char || todisplay+=.
case ${#char} in
1|2 ) char=${ascii%$char*};values+=($((${#char}+32)));;
7 ) char=${char#*\'\\};values+=($((8#${char%\'})));;
5 ) char=${char#*\'\\};char=${cntrl%${char%\'}*};
values+=($((${#char}+7)));;
* ) echo >&2 ERROR: $char;;
esac
else
values+=(0)
fi
if [ ${#values[@]} -gt 15 ] ;then
printf "%08x $fmt8 $fmt8 |%s|\n" $address ${values[@]} "$todisplay"
((address+=16))
values=() todisplay=
fi
done
if [ "$values" ] ;then
((${#values[@]}>8))&&fmt="$fmt8 ${fmt8:0:(${#values[@]}%8)*5}"||
fmt="${fmt8:0:${#values[@]}*5}"
printf "%08x $fmt%$((
50-${#values[@]}*3-(${#values[@]}>8?1:0)
))s |%s|\n" $address ${values[@]} ''""'' "$todisplay"
fi
printf "%08x (%d chars read.)\n" $((address+${#values[@]})){,}
Вы можете попробовать/использовать это, но не пытайтесь сравнивать показатели!
time hd < <(seq 1 10000|gzip)|wc
1415 25480 111711
real 0m0.020s
user 0m0.008s
sys 0m0.000s
time ./hex.sh < <(seq 1 10000|gzip)|wc
1415 25452 111669
real 0m2.636s
user 0m2.496s
sys 0m0.048s
одно и то же задание: 20 мс для hd
против 2000 мс для моего bash script
.
... но если вы хотите прочитать 4 байта в заголовке файла или даже адрес сектора на жестком диске, это может сделать работу...