如何使用grep命令搜索包含二进制数据的文本文件?

157

grep返回

二进制文件test.log匹配

例如

echo    "line1 re \x00\r\nline2\r\nline3 re\r\n" > test.log  # in zsh
echo -e "line1 re \x00\r\nline2\r\nline3 re\r\n" > test.log  # in bash
grep re test.log

我希望结果显示第一行和第三行(共两行)。

是否可能使用tr将无法打印的数据转换为可读数据,以便再次使用grep?


1
请注意,有一个程序可以从二进制文件中过滤出二进制字符并仅保留文本字符(可读)。在此处下载: http://www.soft.tahionic.com/download-words_extractor/index.html - Gabriel
不好意思,但是...你的echo命令中不是缺少了-e吗? - Sopalajo de Arrierez
如果您使用的是 'zsh',那么没有必要使用 -e。如果您使用的是 'bash',则应该加上 -e。 - Daniel YC Lin
https://serverfault.com/questions/328101/how-do-i-grep-through-binary-files-that-look-like-text - Ciro Santilli OurBigBook.com
11个回答

154
grep -a

再简单不过了。


7
这与paxdiablo 2年前提到的 grep --text 相同。 - user829755
6
好的,除非你执行以下步骤,否则这将无法在OSX上工作:LC_ALL="C" grep -a - Chris Stratton
@ChrisStratton,这是改变游戏规则的答案!非常感谢。您能详细解释一下吗?为什么需要使用这种结构,它意味着什么? - Lidjan
C是“基本”的语言环境/编码,大多数其他语言环境都包含更多的值作为“纯文本”。 - PePa

104

一种方法是使用 grep --text 将二进制文件视为文本,但这可能会导致将二进制信息发送到终端。如果你在运行解释输出流的终端(例如VT / DEC或其他终端),这并不是一个好主意。

另外,您可以使用以下命令将文件发送通过 tr

tr '[\000-\011\013-\037\177-\377]' '.' <test.log | grep whatever

这段代码将把除换行符以外的任何小于空格字符和大于126的字符替换为.字符,只保留可打印字符。


如果你希望将每个“非法”字符替换为不同的字符,可以使用以下类似标准输入过滤器的C程序:

#include<stdio.h>
int main (void) {
    int ch;
    while ((ch = getchar()) != EOF) {
        if ((ch == '\n') || ((ch >= ' ') && (ch <= '~'))) {
            putchar (ch);
        } else {
            printf ("{{%02x}}", ch);
        }
    }
    return 0;
}

这将给你{{NN}},其中NN是字符的十六进制代码。你只需调整printf 以适应所需的输出样式即可。

您可以在此处查看该程序的运行情况,它会:

pax$ printf 'Hello,\tBob\nGoodbye, Bob\n' | ./filterProg
Hello,{{09}}Bob
Goodbye, Bob

这种方法将所有二进制字符映射为相同的“.”符号。是否有其他方法将它们映射为可读的符号? - Daniel YC Lin
当然,你可以通过另一个过滤程序运行它,其中一个我已经在更新中提供了。 - paxdiablo
1
我认为 tr '[:cntrl:] '.' 更好。而且在你的 tr 语法中应该是 \000-\010\013\014\016-\037\177-\377' - Daniel YC Lin
2
经过测试,tr '[\000-\010\013\014\016-\037\177-\377]' '_' 是可行的,但控制字符不适用于我的情况。 - Daniel YC Lin
3
你可以通过将 grep --text 导入管道中的 tr 来省略掉 cat 步骤。这样还可以在多个文件中进行搜索,并在输出结果中保留文件名。 - aaaantoine
@user55570,我建议您下载GNU tr的副本并使用它。或者换一个更好的操作系统 :-) - paxdiablo

78

您可以通过cat -v运行数据文件,例如:

$ cat -v tmp/test.log | grep re
line1 re ^@^M
line3 re^M

这可以进一步进行后处理以去除垃圾;这类似于您有关使用 tr 的查询。

-v 只是告诉 cat 显示非打印字符。


7
问题已解决。谢谢!以下是“man cat”对于“-v”的说明:-v,--show-nonprinting,使用^和M-符号表示非打印字符,但不包括换行符和制表符。 - tommy.carstensen
请注意,这也可以在管道中使用。例如:set | cat -v | grep variable - funroll
1
为什么要使用这个,如果grep --text就可以工作?这似乎更加复杂。 - Michael Haefele
1
grep --text 并非总是有效,它将 CTRL+D 解释为文件终止符。因此,如果您的二进制文件中包含该字符,grep 将提前退出。 - Tommy

38
你可以使用 "strings" 来从二进制文件中提取字符串,例如
strings binary.file | grep foo

对我来说效果很好,因为源是带有每行UID的调试日志。谢谢。 - brandeded
对我也很有用。谢谢你的回答。救了我的一天 :) - Shekhar
2
我很感激@paxdiablo的回答,但是为了快速解决问题并继续工作,你不能错过这个。 - Wil
尝试使用paxdiablo的解决方案,但它没有给我期望的任何结果。@moodywoody你的解决方案快速、简单,并且完全输出了我所需的内容! - justinhartman

21

你可以使用以下命令让grep查看二进制文件:

grep --binary-files=text
你可能还想要添加 -o (--only-matching),这样你就不会得到大量的二进制乱码,这些乱码会破坏你的终端。

可能会输出二进制垃圾数据,如果输出是终端并且终端驱动程序将其解释为命令,则可能会产生严重的副作用。 - Daniel YC Lin
1
如果你使用--only-matching,并且你的正则表达式不匹配任意二进制数据,那么就不会有问题。 - A B
如果正则表达式是'first.end',二进制数据包含在'.'模式中,那么它无法正确地处理我的后处理。不管怎样,谢谢。 - Daniel YC Lin

18

从Grep 2.21开始,二进制文件会有不同的处理方式:

在搜索二进制数据时,grep现在可能将非文本字节视为行终止符。这可以显著提高性能。

因此,对于二进制数据,所有非文本字节(包括换行符)都被视为行终止符。如果想要更改此行为,可以:

  • 使用--text。这将确保只有换行符是行终止符

  • 使用--null-data。这将确保只有空字节是行终止符


8

使用grep -a命令可以强制grep搜索和输出二进制文件。例如:grep -a re test.log


3

正如James Selvakumar所说,grep -a可以解决问题。-a或--text强制Grep将输入流处理为文本。 请参阅Manpage http://unixhelp.ed.ac.uk/CGI/man-cgi?grep

试试

cat test.log | grep -a somestring

2
你可以做到:
strings test.log | grep -i

这将把输出转换为可读字符串以供grep使用。

1
这是我在一个没有安装“strings”命令的系统中使用的内容。
cat yourfilename | tr -cd "[:print:]"

这将一次性打印文本并删除不可打印字符,不像“cat -v filename”需要进行一些后处理才能删除不需要的内容。请注意,其中一些二进制数据可能是可打印的,因此在好的内容之间仍会得到一些无意义的东西。如果可以使用strings,则认为它也可以删除这些无意义的东西。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接