¿Lograr un formato similar a un volcado hexadecimal, que incluye cadenas binarias, en la línea de comando?

¿Lograr un formato similar a un volcado hexadecimal, que incluye cadenas binarias, en la línea de comando?

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 hexdumpmanual. .

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 hexdumpprograma (en términos de agrupación de bytes? ) al utilizar su -eopción?

Respuesta1

Si falla un programa de volcado con opciones de volcado adecuadas, siempre puede improvisar algo usando ambos hexdumpy xddluego uniendo la salida con pegar. No es bonito, pero usar un Shell que admita la sustitución de procesos ( bashfuncionará):

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 sedpara convencer dca traducir odla 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 -N12opción; simplemente la usé para no ahogarme con una tubería interminable de datos pseudoaleatorios. Y -w4especifica 4 bytes por línea, pero debería poder utilizar cualquier cantidad de bytes. Además, el 1i2o sedcomando es una dcinstrucción sobre su base de salida ( 2para 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 seddeclaración y cambiar odla -t d1zopció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 bcpuede 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

dcno es tan hablador:

printf '%do%dn10P' 2 64 8 128 16 "'A" 2 "'a" |dc

PRODUCCIÓN

1000000
200
41
1100001

Hazlo man dc bcpara 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 odque no imprima compensaciones, lo cual ahora estoy dudando, que quiero salidas de -type o, do xun byte a la vez y que quiero la representación ASCII zde cada byte agregada al final de la línea, -verbosely(para que no solo me imprima un 0*for 0000)en -w4bytes 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 odes posible en un archivo |pipeline.

información relacionada