【问题标题】:How to grep a text file which contains some binary data?如何grep包含一些二进制数据的文本文件?
【发布时间】:2023-03-31 02:36:01
【问题描述】:

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

我希望结果会显示 line1 和 line3(总共两行)。

是否可以使用tr将不可打印的数据转换为可读的数据,让grep再次工作?

【问题讨论】:

标签: shell


【解决方案1】:
grep -a

没有比这更简单的了。

【讨论】:

  • 这与paxdiablo 2 年前提到的grep --text 相同
  • 是的,除非您执行以下操作,否则这在 OSX 上不起作用:LC_ALL="C" grep -a
  • @ChrisStratton 那是改变游戏规则的答案!非常感谢。你能详细说明一下吗?为什么你需要使用这种结构,它意味着什么?
  • C 是“基本”语言环境/编码,大多数其他语言环境包含更多值作为“纯文本”。
【解决方案2】:

一种方法是简单地将二进制文件视为文本,使用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

【讨论】:

  • 此方法将所有二进制字符映射到相同的 '.'象征。是否有其他方法将它们映射到可读符号?
  • 当然,您可以通过不同的过滤程序运行它,我在更新中提供了其中一个。
  • 我认为tr '[:cntrl:] '.' 更好。在你的 tr 语法中应该是 \000-\010\013\014\016-\037\177-\377'
  • 经过测试,tr '[\000-\010\013\014\016-\037\177-\377]' '_' 可行,cntrl 不适合我的情况。
  • 您可以通过管道将grep --text 保存为cat 步骤,而不是反之亦然。这也让您可以 grep 多个文件并将文件名引用保留在输出中。
【解决方案3】:

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

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

然后可以进一步后处理以删除垃圾;这与您对使用tr 执行任务的查询最为相似。

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

【讨论】:

  • 解决了我的问题。谢谢!以下是man cat-v 的评价:-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
  • 请注意,这也适用于管道。例如。 set | cat -v | grep variable
  • 如果 grep --text 有效,为什么要使用这个?这似乎要复杂得多。
  • grep --text 并不总是有效;它尊重 CTRL+D 作为文件终止符。所以如果你的二进制文件中有这个,grep 会提前退出。
【解决方案4】:

您可以使用“字符串”从二进制文件中提取字符串,例如

strings binary.file | grep foo

【讨论】:

  • 对我来说效果很好,因为源是每行带有 UID 的调试日志。谢谢。
  • 对我也很有效。感谢您的回答。拯救了我的一天:)
  • 我很欣赏@paxdiablo 的回答,但要快速回答并继续工作,你不能错。
  • 尝试使用 paxdiablo 解决方案,但它没有给我任何预期的结果。 @moodywoody 您的解决方案快速、简单,并且输出正是我所需要的!
【解决方案5】:

您可以强制 grep 查看二进制文件:

grep --binary-files=text

您可能还想添加-o (--only-matching),这样您就不会收到大量会破坏您的终端的二进制乱码。

【讨论】:

  • 可能会输出二进制垃圾,如果输出是终端并且终端驱动程序将其中的一些解释为命令,则会产生令人讨厌的副作用。
  • 如果你使用--only-matching,并且你的正则表达式不匹配任意二进制数据,你不会有问题。
  • 如果正则表达式是'first.*end'并且二进制数据包含在'.*'模式中,它不能正确处理我的后处理。无论如何,谢谢。
【解决方案6】:

从 Grep 2.21 开始,二进制文件为treated differently:

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

所以现在发生的是对于二进制数据,所有非文本字节 (包括换行符)被视为行终止符。如果你想改变这个 行为,你可以:

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

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

【讨论】:

    【解决方案7】:

    grep -a 将强制 grep 从 grep 认为是二进制文件的文件中搜索和输出。 grep -a re test.log

    【讨论】:

      【解决方案8】:

      正如 James Selvakumar 已经说过的,grep -a 可以解决问题。 -a 或 --text 强制 Grep 将输入流作为文本处理。 参见手册页http://unixhelp.ed.ac.uk/CGI/man-cgi?grep

      试试

      cat test.log | grep -a somestring
      

      【讨论】:

        【解决方案9】:

        你可以的

        strings test.log | grep -i
        

        这会将作为可读字符串的输出转换为 grep。

        【讨论】:

          【解决方案10】:

          这是我在没有安装“strings”命令的系统中使用的

          cat yourfilename | tr -cd "[:print:]"
          

          这会一举打印文本并删除不可打印的字符,这与“cat -v 文件名”不同,后者需要一些后处理来删除不需要的内容。请注意,某些二进制数据可能是可打印的,因此您仍然会在好东西之间得到一些乱码。如果你可以使用字符串,我认为字符串也会消除这种乱码。

          【讨论】:

            【解决方案11】:

            您也可以尝试Word Extractor 工具。 Word Extractor 可以与您计算机中的任何文件一起使用,以将包含人类文本/单词的字符串与二进制代码(exe 应用程序、DLL)分开。

            【讨论】:

            • 我的情况,我不需要单词提取器,我需要保留行号。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-04-28
            • 1970-01-01
            • 1970-01-01
            • 2015-11-30
            • 2011-07-26
            • 1970-01-01
            • 2011-10-30
            相关资源
            最近更新 更多