Ein Hexdump-ähnliches Format, das Binärzeichenfolgen enthält, auf der Befehlszeile erreichen?

Ein Hexdump-ähnliches Format, das Binärzeichenfolgen enthält, auf der Befehlszeile erreichen?

Mir gefällt sehr gut hexdump, insbesondere weil man ein benutzerdefiniertes Format definieren kann, beispielsweise:

$ 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          : .

Ich kann also beispielsweise 4 Bytes pro Zeile wählen, die zuerst hexadezimal und dann als Zeichen geschrieben werden. Was mir hier jedoch fehlt, ist ein Formatierungszeichen für „Binärzeichenfolgen“ (oder „Bitfolgen“). Ich möchte beispielsweise -e '4/1 "%08b "'irgendwo in dieser Befehlszeile etwas wie Folgendes schreiben und beispielsweise Folgendes erhalten:

0: 00 01 02 03 : 00000000 00000001 00000010 00000011 : ....
4: 0A          : 00001010 : .

Natürlich müsste man dann wahrscheinlich noch die Byte-Reihenfolge angeben (wenn Gruppen von mehr als einem Byte formatiert werden sollen) usw.... Aber diese Art der Formatierung gibt es jedenfalls nicht, soweit ich das im Handbuch sehen kann hexdump.

Meine Frage ist also: Welche Alternativen habe ich in einer Linux-Befehlszeile, damit ich einen formatierten Dump erhalten kann, der Binärzeichenfolgen wie oben enthält und dennoch die Anpassbarkeit des hexdumpProgramms (hinsichtlich der Bytegruppierung) bei Verwendung seiner -eOption so weit wie möglich bewahrt?

Antwort1

Wenn Sie kein Dump-Programm mit geeigneten Dump-Optionen haben, können Sie immer etwas zusammenschustern, indem Sie sowohl als auch verwenden hexdumpund xdddie Ausgabe dann mit Paste zusammenfügen. Das ist nicht schön, aber die Verwendung einer Shell, die Prozesssubstitution unterstützt ( bashreicht aus):

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-)

Ausgabe:

0: 00 01 02 03 : 00000000 00000001 00000010 00000011  ....
4: 0A          : 00001010                             .

Antwort2

Hier ist mein Vorschlag für die Verwendung von Perl unter Verwendung der Formatierungsbezeichner für die Funktion pack()/ unpack(). Der Testaufruf sähe dann folgendermaßen aus:

$ 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 ist ziemlich schwierig, dazwischen String-Markierungen einzufügen – aber das Schöne ist, dass Sie Bytes trotzdem „gruppieren“ können – Sie können z. B. zwei Bytes gruppieren und sie als vorzeichenbehaftete (kurze) Ganzzahlen interpretieren, Beispiel:

$ 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  '.'

Hier ist 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);

Antwort3

So sedkönnen Sie die Ausgabe von in die Basis 2 dcübersetzen :od

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

Es ist jetzt etwas einfacher – und schneller – aber immer noch keine Schönheitskönigin. Es druckt auch die Dezimal- und Basis-2-Werte aller Bytes.

Wenn ich es ausführe, erhalte ich:

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

Oder...

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

Die Feldbreiten könnten ein wenig Arbeit vertragen, aber das liegt ganz bei Ihnen. Sie brauchen die -N12Option nicht – ich habe sie nur verwendet, damit ich nicht an einer endlosen Pipe pseudozufälliger Daten ersticke. Und die -w4gibt 4 Bytes pro Zeile an, aber Sie sollten in der Lage sein, jede beliebige Anzahl von Bytes zu verwenden. Außerdem 1i2o sedist der Befehl eine dcAnweisung bezüglich seiner Ausgabebasis – 2für Binärzahlen –, aber jede Basis zwischen 2 und 16 sollte genauso gut funktionieren. Wenn Sie beispielsweise eine hexadezimale und eine Basis-2-Ausgabe sehen möchten, müssen Sie dieser ersten sedAnweisung „16i“ hinzufügen und die Option odvon in ändern .-t d1zt x1z

Weitere Optionen sind ...

printf macht Folgendes:

printf '%o\n%x\n' 128 128

200

80

...sogar...

printf '%o\n%x\n' "'a" "'a"

141

61

Binär ist nicht ganz so einfach, bckann aber alles, wenn Sie es obase=entsprechend Ihren Spezifikationen einstellen:

printf 'obase=2;%d
        obase=8;%d
        obase=16;%d
        obase=2;%d
        ' 64 128 "'A" "'a" |
bc

AUSGABE

1000000
200
41
1100001

dcist nicht ganz so gesprächig:

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

AUSGABE

1000000
200
41
1100001

man dc bcFür weitere Informationen klicken Sie hier .

Und für Dateiströme können Sie immer Folgendes verwenden od:

for o in o d x ; do
    echo aBcD | 
    od -A n -t ${o}1z -v -w4
done

AUSGABE

 141 102 143 104  >aBcD<
 012              >.<
  97  66  99  68  >aBcD<
  10              >.<
  61  42  63  44  >aBcD<
  0a              >.<

Mit^das^eine, die ich anweise, odkeine Offsets zu drucken - was ich jetzt bezweifle -, dass ich Ausgaben vom -tTyp o, doder xein Byte nach dem anderen haben möchte und dass ich die ASCII-Darstellung zjedes Bytes an das Ende der Zeile angehängt haben möchte, -verbosely(damit es mir nicht nur ein 0*für ausgibt 0000)bei -w4Bytes pro Zeile.

Ohne -A nwird Folgendes gedruckt:

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

Und jede Kombination von dc bc odist natürlich in einem möglich |pipeline.

verwandte Informationen