
Me gusta mucho hexdump
, sobre todo porque puedes definir un formato personalizado; decir:
$ echo -e '\x00\x01\x02\x03' | hexdump -v -e '1/1 "%_ad: "' -e '4/1 "%02X "' -e '1/1 " : "' -e '4/1 "%_p"' -e '1/1 "\n"'
0: 00 01 02 03 : ....
4: 0A : .
Entonces, puedo elegir tener, digamos, 4 bytes por línea, escritos primero como hexadecimal y luego como caracteres. Pero lo que me falta aquí es un carácter de formato de "cadena binaria" (o "cadena de bits"); Por ejemplo, me gustaría escribir algo como -e '4/1 "%08b "'
en algún lugar de esa línea de comando y obtener, por ejemplo:
0: 00 01 02 03 : 00000000 00000001 00000010 00000011 : ....
4: 0A : 00001010 : .
Por supuesto, entonces probablemente habría que especificar el endianidad (si se deben formatear grupos de más de un byte), etc... Pero en cualquier caso, este tipo de formato no existe, por lo que puedo ver en el hexdump
manual. .
Entonces mi pregunta es: ¿qué alternativas tengo en una línea de comando de Linux, para poder obtener un volcado formateado que incluya cadenas binarias como se indicó anteriormente y, sin embargo, conserve en la mayor medida posible la personalización del hexdump
programa (en términos de agrupación de bytes? ) al utilizar su -e
opción?
Respuesta1
Si falla un programa de volcado con opciones de volcado adecuadas, siempre puede improvisar algo usando ambos hexdump
y xdd
luego uniendo la salida con pegar. No es bonito, pero usar un Shell que admita la sustitución de procesos ( bash
funcionará):
mkfifo fifo
echo -e '\x00\x01\x02\x03' |
tee fifo |
paste -d' ' \
<(hexdump -v -e '1/1 "%_ad: "' -e '4/1 "%02X "' -e '1/1 " :\n"') \
<(xxd -b -c 4 fifo | cut -d' ' -f 2-)
Producción:
0: 00 01 02 03 : 00000000 00000001 00000010 00000011 ....
4: 0A : 00001010 .
Respuesta2
Aquí está mi sugerencia de usar Perl, utilizando sus especificadores de formato para la función pack()
/ ; unpack()
la llamada de prueba sería como:
$ echo -e '\x00\x01\x02\x03' | perl hexdump-00.pl --offset 120 --group 4 --add '(H2)*' --add '(B8)*'
Opening '' STDIN
Cannot seek!
0
00000000: 00 01 02 03 00000000 00000001 00000010 00000011 '....'
00000004: 0a 00001010 '.'
Es un poco difícil insertar marcadores de cadena en el medio, pero lo bueno es que aún puedes "agrupar" bytes dentro; por ejemplo, puedes agrupar dos bytes e interpretarlos como un entero con signo (corto), por ejemplo:
$ perl -e 'print pack("s*\n", (-124))' | hexdump -C
00000000 84 ff |..|
00000002
$ echo -e '\x00\x01\x84\xff' | perl hexdump.pl \
--offset 120 --group 4 \
--add '(H2)*' \
--add '(B8)*' \
--add '(s2)*'
Opening '' STDIN
Cannot seek!
0
00000000: 00 01 84 ff 00000000 00000001 10000100 11111111 256 -124 '....'
00000004: 0a 00001010 '.'
Aquí está hexdump-00.pl
:
#!/usr/bin/perl
# perl hexdump-00.pl --offset 120 --group 4 --add '(H2)*' --add '(B8)*' test.file
use strict;
use warnings;
use Getopt::Long;
use Fcntl qw(SEEK_CUR SEEK_SET);
my $offset = 0;
my $groupsize = 1;
my $length = 128;
my @list=();
my $result = GetOptions (
"offset=i" => \$offset,
"group=i" => \$groupsize,
"length=i" => \$length,
"add=s" => \@list,
);
my $inputfname="";
my $inputfh;
$inputfname = $ARGV[0] if defined $ARGV[0];
if (($inputfname eq "") || ($inputfname eq "-")) {
printf(STDERR "Opening '%s' STDIN\n", $inputfname);
$inputfh = *STDIN;
} else {
printf(STDERR "Opening '%s'\n", $inputfname);
open ($inputfh, "<$inputfname");
}
binmode($inputfh);
my $startaddr=0;
if( not(defined($startaddr = sysseek($inputfh, $offset-1, SEEK_SET))) ) {
printf(STDERR "Cannot seek!\n");
#~ $startaddr = sysseek($inputfh, 0, 0); // cannot reset like this
$startaddr = 0; # just avoid errors
}
print(STDERR $startaddr . "\n");
my $buffer=undef;
my $nread;
my $total=0;
while (($nread=sysread($inputfh, $buffer, $groupsize)) > 0) { # , $startaddr
#~ printf("%08X: nr: %d, buf '%s'\n",$startaddr,$nread,$buffer);
printf("%08X: ", $startaddr);
foreach my $tformat (@list) {
foreach my $tentry (unpack($tformat, $buffer)) {
printf("%s ", $tentry);
}
}
(my $newbuf = $buffer) =~ s/[^[:print:]]/./g; # make non-printable into '.'
printf(" '%s'", $newbuf);
print("\n");
$startaddr += $nread;
$total += $nread;
if ($total > $length) { last; }
}
close($inputfh);
Respuesta3
Aquí hay algunos sed
para convencer dc
a traducir od
la salida de a base 2:
od -t d1z -w4 -v -N12 </dev/urandom |
sed -e '1i2o' -e 's/.*/[&]p/p;$d
s/>/]n [>/;s/[^ ]*/&]n [/;h;s/>.*//;
s/ -/ _/g;s/ [^] [][^ ]*/ ]n&n [ /g;G
s/\n[^>]*//' |
dc
Ahora es un poco más sencillo, por no decir más rápido, pero sigue sin ser una reina de belleza. También imprime los valores decimales y de base 2 de todos los bytes.
Cuando lo ejecuto me sale:
0000000 -43 125 -117 -39 >.}..<
0000000 -101011 1111101 -1110101 -100111 >.}..<
0000004 62 28 80 61 >>.P=<
0000004 111110 11100 1010000 111101 >>.P=<
0000010 6 14 120 -16 >..x.<
0000010 110 1110 1111000 -10000 >..x.<
0000014
O...
echo aBcD | od -t d1z -w4 -v | sed ... | dc
0000000 97 66 99 68 >aBcD<
0000000 1100001 1000010 1100011 1000100 >aBcD<
0000004 10 >.<
0000004 1010 >.<
0000005
Los anchos de campo podrían necesitar un poco de trabajo, pero es todo tuyo. No necesitas la -N12
opción; simplemente la usé para no ahogarme con una tubería interminable de datos pseudoaleatorios. Y -w4
especifica 4 bytes por línea, pero debería poder utilizar cualquier cantidad de bytes. Además, el 1i2o
sed
comando es una dc
instrucción sobre su base de salida ( 2
para binario), pero cualquier base entre 2 y 16 debería funcionar igual de bien. Si desea ver, por ejemplo, salida hexadecimal y base 2, deberá agregar '16i' a esa primera sed
declaración y cambiar od
la -t d1z
opción a t x1z
.
Otras opciones incluyen...
printf hace esto:
printf '%o\n%x\n' 128 128
200
80
...incluso...
printf '%o\n%x\n' "'a" "'a"
141
61
Binario no es tan simple, pero bc
puede hacerlo todo si lo configura obase=
según sus especificaciones:
printf 'obase=2;%d
obase=8;%d
obase=16;%d
obase=2;%d
' 64 128 "'A" "'a" |
bc
PRODUCCIÓN
1000000
200
41
1100001
dc
no es tan hablador:
printf '%do%dn10P' 2 64 8 128 16 "'A" 2 "'a" |dc
PRODUCCIÓN
1000000
200
41
1100001
Hazlo man dc bc
para más información.
Y nuevamente, para secuencias de archivos siempre puedes usar od
:
for o in o d x ; do
echo aBcD |
od -A n -t ${o}1z -v -w4
done
PRODUCCIÓN
141 102 143 104 >aBcD<
012 >.<
97 66 99 68 >aBcD<
10 >.<
61 42 63 44 >aBcD<
0a >.<
Con^eso^uno le digo od
que no imprima compensaciones, lo cual ahora estoy dudando, que quiero salidas de -t
ype o
, d
o x
un byte a la vez y que quiero la representación ASCII z
de cada byte agregada al final de la línea, -v
erbosely(para que no solo me imprima un 0*
for 0000
)en -w4
bytes por línea.
Sin -A n
él imprime:
0000000 141 102 143 104 >aBcD<
0000004 012 >.<
0000005
0000000 97 66 99 68 >aBcD<
0000004 10 >.<
0000005
0000000 61 42 63 44 >aBcD<
0000004 0a >.<
0000005
Y, por supuesto, cualquier combinación de dc
bc
od
es posible en un archivo |pipeline
.