
私は がとても気に入っていますhexdump
。特に、カスタム フォーマットを定義できるからです。たとえば、次のようにします。
$ 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 : .
つまり、たとえば 1 行あたり 4 バイトを、最初に 16 進数で書き、次に文字で書き込むことを選択できます。ただし、ここで不足しているのは、「バイナリ文字列」(または「ビット文字列」) の書式設定文字です。たとえば、-e '4/1 "%08b "'
コマンド ラインのどこかに次のような内容を書き込むと、次のようになります。
0: 00 01 02 03 : 00000000 00000001 00000010 00000011 : ....
4: 0A : 00001010 : .
もちろん、その場合、エンディアン (1 バイトを超えるグループをフォーマットする必要がある場合) などを指定する必要があります。しかし、いずれにしても、マニュアルで確認する限り、この種のフォーマットは存在しませんhexdump
。
そこで私の質問は、上記のようなバイナリ文字列を含むフォーマットされたダンプを取得し、そのオプションhexdump
を使用するときにプログラムのカスタマイズ性(バイトグループ化の観点から)を最大限に維持するために、Linux コマンドラインでどのような代替手段があるか-e
ということです。
答え1
適切なダンプ オプションを持つダンプ プログラムが失敗した場合は、との両方を使用して、出力を paste で結合することで、いつでも何とかすることができますhexdump
。xdd
見栄えはよくありませんが、プロセス置換をサポートするシェル ( bash
) を使用するとうまくいきます。
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-)
出力:
0: 00 01 02 03 : 00000000 00000001 00000010 00000011 ....
4: 0A : 00001010 .
答え2
pack()
ここでは、Perl を使用して/関数の書式指定子を利用する方法を提案しますunpack()
。テスト呼び出しは次のようになります。
$ 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 '.'
間に文字列マーカーを挿入するのは少々難しいですが、内部でバイトを「グループ化」できるのは良いことです。たとえば、2 つのバイトをグループ化して、符号付き (short) 整数として解釈できます。例:
$ 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 '.'
ここは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);
答え3
の出力を 2 進数に変換するsed
方法は次のとおりです。dc
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
今では少しシンプルになりました。言うまでもなく高速化されていますが、まだ完璧ではありません。また、すべてのバイトの 10 進数と 2 進数の値も出力します。
実行すると次のようになります:
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
または...
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
フィールド幅は少し調整が必要ですが、すべてはあなたの自由です。 オプションは必要ありません-N12
。疑似ランダム データの無限のパイプに詰まらないように、これを使用しただけです。 また、 は-w4
1 行あたり 4 バイトを指定しますが、任意のバイト数を使用できるはずです。 また、コマンドは出力基数 (バイナリ) に関する命令1i2o
sed
ですが、2 から 16 までの基数であれば、同じように機能します。 たとえば、16 進数と基数 2 の出力を確認したい場合は、最初のステートメントに「16i」を追加し、のオプションを に変更する必要があります。dc
2
sed
od
-t d1z
t x1z
その他のオプションは次のとおりです...
printf は次のように動作します:
printf '%o\n%x\n' 128 128
200
80
...平...
printf '%o\n%x\n' "'a" "'a"
141
61
バイナリはそれほど単純ではありませんが、仕様に合わせてbc
設定すればすべて実行できます。obase=
printf 'obase=2;%d
obase=8;%d
obase=16;%d
obase=2;%d
' 64 128 "'A" "'a" |
bc
出力
1000000
200
41
1100001
dc
それほどおしゃべりではありません:
printf '%do%dn10P' 2 64 8 128 16 "'A" 2 "'a" |dc
出力
1000000
200
41
1100001
man dc bc
詳細については、こちらをご覧ください。
また、ファイル ストリームの場合は、常に以下を使用できますod
。
for o in o d x ; do
echo aBcD |
od -A n -t ${o}1z -v -w4
done
出力
141 102 143 104 >aBcD<
012 >.<
97 66 99 68 >aBcD<
10 >.<
61 42 63 44 >aBcD<
0a >.<
と^それ^1つはオフセットを印刷しないように指示することですod
が、これは今になって考え直しています。つまり、出力は-t
、o
、d
またはx
1バイトずつで、z
各バイトのASCII表現は行末に追加されるということ-v
です。0*
(つまり、単にfor を印刷するだけではありません0000
)1 行あたりバイト-w4
。
これがないと、次のように-A n
印刷されます:
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
そして、 では の任意の組み合わせがdc
bc
od
当然可能です|pipeline
。