代码之家  ›  专栏  ›  技术社区  ›  sdaau

Linux上的二进制grep?

  •  28
  • sdaau  · 技术社区  · 14 年前

    假设我生成了以下二进制文件:

    # generate file:
    python -c 'import sys;[sys.stdout.write(chr(i)) for i in (0,0,0,0,2,4,6,8,0,1,3,0,5,20)]' > mydata.bin
    
    # get file size in bytes
    stat -c '%s' mydata.bin
    
    # 14
    

    我想找出所有零的位置( 0x00 ),使用类似grep的语法。

    到目前为止,我所能做的就是:

    $ hexdump -v -e "1/1 \" %02x\n\"" mydata.bin | grep -n '00'
    
    1: 00
    2: 00
    3: 00
    4: 00
    9: 00
    12: 00
    

    但是,这会隐式地将原始二进制文件中的每个字节转换为多字节ASCII表示形式,在该表示形式上 grep 操作;不完全是优化的主要示例:)

    有二进制的吗 格雷普 对于Linux?也可能是支持语法之类的正则表达式,但也支持字节“字符”-也就是说,我可以写类似的东西 a(\x00*)b '并在字节'a'(97)和'b'(98)之间匹配'0字节或更多'?

    编辑:上下文是我正在开发一个驱动程序,我在其中捕获8位数据;数据中有问题,可能是千字节到兆字节,我想检查特定的签名以及它们出现的位置。( 到目前为止,我正在处理千字节的片段,所以优化并不是那么重要——但是如果我开始在兆字节长的捕获中出现一些错误,我需要分析这些错误,我的猜测是我想要更优化的东西:)。尤其是,我想要一个可以“grep”一个字节的字符- hexdump 强制我按字节搜索字符串 )

    编辑2:同样的问题,不同的论坛:) grepping through a binary file for a sequence of bytes

    EDIT3:感谢@tchrist的回答,这里还有一个“grepping”和匹配并显示结果的示例( 虽然和OP的问题不太一样 ):

    $ perl -ln0777e 'print unpack("H*",$1), "\n", pos() while /(.....\0\0\0\xCC\0\0\0.....)/g' /path/to/myfile.bin
    
    ca000000cb000000cc000000cd000000ce     # Matched data (hex)
    66357                                  # Offset (dec)
    

    若要将匹配的数据分别分组为一个字节(两个十六进制字符),则需要为匹配字符串中的所有字节指定“H2 H2…”;作为“我的匹配” .....\0\0\0\xCC\0\0\0..... '覆盖17个字节,我可以写' "H2"x17 '在Perl中。这些“H2”中的每一个都将返回一个单独的变量(如在列表中),因此 join 还需要用于在它们之间添加空格-最终:

    $ perl -ln0777e 'print join(" ", unpack("H2 "x17,$1)), "\n", pos() while /(.....\0\0\0\xCC\0\0\0.....)/g' /path/to/myfile.bin
    
    ca 00 00 00 cb 00 00 00 cc 00 00 00 cd 00 00 00 ce
    66357
    

    好。。实际上,Perl是非常好的“二进制重映射”工具,我必须承认:)只要正确地学习语法:)

    6 回复  |  直到 11 年前
        1
  •  14
  •   tchrist    14 年前

    一个线性输入

    下面是较短的一行程序版本:

    % perl -ln0e 'print tell' < inputfile
    

    这里有一条稍微长一点的线:

    % perl -e '($/,$\) = ("\0","\n"); print tell while <STDIN>' < inputfile
    

    连接这两个单行程序的方法是对第一个单行程序进行解压缩:

    % perl -MO=Deparse,-p -ln0e 'print tell'
    BEGIN { $/ = "\000"; $\ = "\n"; }
    LINE: while (defined(($_ = <ARGV>))) {
        chomp($_);
        print(tell);
    }
    

    编程输入

    如果您想将其放入一个文件中,而不是从命令行调用它,那么这里有一个更明确的版本:

    #!/usr/bin/env perl
    
    use English qw[ -no_match_vars ];
    
    $RS  = "\0";    # input  separator for readline, chomp
    $ORS = "\n";    # output separator for print
    
    while (<STDIN>) {
        print tell();
    }
    

    这里是真正的长版本:

    #!/usr/bin/env perl
    
    use strict;
    use autodie;  # for perl5.10 or better
    use warnings qw[ FATAL all  ];
    
    use IO::Handle;
    
    IO::Handle->input_record_separator("\0");
    IO::Handle->output_record_separator("\n");
    
    binmode(STDIN);   # just in case
    
    while (my $null_terminated = readline(STDIN)) {
        # this just *past* the null we just read:
        my $seek_offset = tell(STDIN);
        print STDOUT $seek_offset;  
    
    }
    
    close(STDIN);
    close(STDOUT);
    

    单列输出

    顺便说一句,为了创建测试输入文件,我没有使用您的大而长的Python脚本;我只是使用这个简单的Perl一行:

    % perl -e 'print 0.0.0.0.2.4.6.8.0.1.3.0.5.20' > inputfile
    

    您会发现,在执行相同的任务时,Perl通常比Python短2-3倍。你不必在清晰性上妥协;还有什么比上面的一行更简单呢?

    编程输出

    我知道,我知道。如果你还不知道这门语言,可能会更清楚:

    #!/usr/bin/env perl
    @values = (
        0,  0,  0,  0,  2,
        4,  6,  8,  0,  1,
        3,  0,  5, 20,
    );
    print pack("C*", @values);
    

    尽管这样也管用:

    print chr for @values;
    

    也一样

    print map { chr } @values;
    

    尽管对于那些喜欢一切都严谨、仔细和所有的人来说,你可能会看到更多:

    #!/usr/bin/env perl
    
    use strict;
    use warnings qw[ FATAL all ];
    use autodie;
    
    binmode(STDOUT);
    
    my @octet_list = (
        0,  0,  0,  0,  2,
        4,  6,  8,  0,  1,
        3,  0,  5, 20,
    );
    
    my $binary = pack("C*", @octet_list);
    print STDOUT $binary;
    
    close(STDOUT); 
    

    TMTOWTDI公司

    Perl支持多种方法,这样您就可以选择您最熟悉的方法。如果这是我计划作为学校或工作项目签入的内容,我肯定会选择更长、更仔细的版本,或者如果我使用的是单行程序,至少在shell脚本中添加一个注释。

    您可以在自己的系统上找到Perl的文档。只是打字

    % man perl
    % man perlrun
    % man perlvar
    % man perlfunc
    

    等在你的空壳提示下。如果你想在网络上找到更漂亮的版本,可以在 perl , perlrun , perlvar ,和 perlfunc http://perldoc.perl.org .

        2
  •  46
  •   Peter Mortensen icecrime    9 年前

    这似乎对我有用:

    grep --only-matching --byte-offset --binary --text --perl-regexp "<\x-hex pattern>" <file>
    

    简短形式:

    grep -obUaP "<\x-hex pattern>" <file>
    

    例子:

    grep -obUaP "\x01\x02" /bin/grep
    

    输出( Cygwin 二进制):

    153: <\x01\x02>
    33210: <\x01\x02>
    53453: <\x01\x02>
    

    所以你可以再次把这个grep来提取偏移量。但别忘了再次使用二进制模式。

        3
  •  19
  •   fuzzyTew    8 年前

    其他人似乎也同样感到沮丧,并编写了自己的工具(或至少类似的东西): bgrep .

        4
  •  10
  •   hdorio    7 年前

    这个 bbe 程序是 sed -就像二进制文件的编辑器。见 documentation .

    举例说明 bbe公司 :

    bbe -b "/\x00\x00\xCC\x00\x00\x00/:17" -s -e "F d" -e "p h" -e "A \n" mydata.bin
    
    11:x00 x00 xcc x00 x00 x00 xcd x00 x00 x00 xce
    

    解释

    -b search pattern between //. each 2 byte begin with \x (hexa notation).
       -b works like this /pattern/:length (in byte) after matched pattern
    -s similar to 'grep -o' suppress unmatched output 
    -e similar to 'sed -e' give commands
    -e 'F d' display offsets before each result here: '11:'
    -e 'p h' print results in hexadecimal notation
    -e 'A \n' append end-of-line to each result
    

    你也可以通过管道 塞德 要获得更干净的输出:

    bbe -b "/\x00\x00\xCC\x00\x00\x00/:17" -s -e "F d" -e "p h" -e "A \n" mydata.bin | sed -e 's/x//g'
    
    11:00 00 cc 00 00 00 cd 00 00 00 ce
    

    你的解决方案 Perl语言 从你的编辑那里给我一个“失忆” 大文件出错。

    同样的问题 bgrep公司 .

    bbe唯一的缺点是我不知道如何在匹配模式之前打印上下文。

        5
  •  8
  •   Omniwombat    14 年前

    只使用grep解决当前问题的一种方法是创建包含单个空字节的文件。之后, grep -abo -f null_byte_file target_file 将产生以下输出。

    0:
    1:
    2:
    3:
    8:
    11:
    

    当然,这是由“-b”请求的每个字节偏移量,后跟由“-o”请求的空字节

    我将是第一个提倡perl的人,但在这种情况下,不需要引入大家庭。

        6
  •  1
  •   Chance    14 年前

    那怎么办 grep -a ? 不确定它是如何在真正的二进制文件上工作的,但它在操作系统认为是二进制的文本文件上工作得很好。