![在命令行上实现类似十六进制转储的格式(包括二进制字符串)?](https://linux22.com/image/38014/%E5%9C%A8%E5%91%BD%E4%BB%A4%E8%A1%8C%E4%B8%8A%E5%AE%9E%E7%8E%B0%E7%B1%BB%E4%BC%BC%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6%E8%BD%AC%E5%82%A8%E7%9A%84%E6%A0%BC%E5%BC%8F%EF%BC%88%E5%8C%85%E6%8B%AC%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%EF%BC%89%EF%BC%9F.png)
我真的很喜欢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 : .
因此,我可以选择每行 4 个字节,首先写为十六进制,然后写为字符。但是,我在这里缺少的是“二进制字符串”(或“位字符串”)格式化字符;例如我想-e '4/1 "%08b "'
在命令行中的某个地方写一些东西,然后得到,例如:
0: 00 01 02 03 : 00000000 00000001 00000010 00000011 : ....
4: 0A : 00001010 : .
当然,那么可能必须指定字节序(如果应该格式化超过一个字节的组)等...但无论如何,据我在手册中看到的,这种格式不hexdump
存在。
所以我的问题是 - 我在 Linux 命令行上有什么替代方案,以便我可以获得包含上述二进制字符串的格式化转储,但最大程度地保留程序的可定制性hexdump
(就字节分组而言) )当使用它的-e
选项时?
答案1
如果转储程序无法使用合适的转储选项,您始终可以通过使用两者来拼凑一些内容hexdump
,xdd
然后使用粘贴将输出连接起来。它不太漂亮,但是使用支持进程替换的 shell(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
这是我使用 Perl 的建议,利用pack()
/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 '.'
在中间插入字符串标记有点困难 - 但好处是,您仍然可以在其中“分组”字节 - 例如,您可以将两个字节分组并将它们解释为有符号(短)整数,例如:
$ 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
下面是一些sed
可以将的输出dc
转换为基数 2 的内容: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
现在有点简单了——更不用说更快了——但它仍然不是选美皇后。它还打印所有字节的十进制值和以 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
指定每行 4 个字节,但您应该能够使用任意数量的字节。此外,该1i2o
sed
命令是dc
关于其输出基数的指令 -2
对于二进制 - 但 2 到 16 之间的任何基数都应该可以正常工作。例如,如果希望查看十六进制和基数 2 的输出,您需要将“16i”添加到第一个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 >.<
和^那个^我告诉od
不要打印偏移量 - 我现在正在猜测 - 我想要-t
ype o
、d
或x
一次一个字节的输出,并且我想要将每个字节的 ASCII 表示z
附加到行尾,-v
erbosely(所以它不只是打印我一个0*
for 0000
)每行字节数-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
。