是什么让 grep 将文件视为二进制文件?

use*_*394 225 grep

我的机器上有一些来自 Windows 系统的数据库转储。它们是文本文件。我正在使用 cygwin 来查看它们。这些似乎是纯文本文件;我用记事本和写字板等文本编辑器打开它们,它们看起来很清晰。但是,当我对它们运行 grep 时,它会说binary file foo.txt matches.

我注意到这些文件包含一些 asciiNUL字符,我认为它们是数据库转储中的工件。

那么是什么让 grep 认为这些文件是二进制的呢?的NUL性格吗?文件系统上有标志吗?我需要更改什么才能让 grep 显示匹配行?

bba*_*a42 162

如果NUL文件中的任何位置都有字符,grep 会将其视为二进制文件。

可能有这样的解决方法cat file | tr -d '\000' | yourgrep来首先消除所有空值,然后搜索文件。

  • ... 或者使用 `-a`/`--text`,至少在 GNU grep 中是这样。 (172认同)
  • NUL 字符的存在是唯一的标准吗?我对此表示怀疑。它可能比那更聪明。任何落在 Ascii 32-126 范围之外的东西都是我的猜测,但我们必须查看源代码才能确定。 (6认同)
  • 我的信息来自特定 grep 实例的手册页。您对实施的评论是有效的,源代码胜过文档。 (3认同)
  • 我有一个文件,cygwin 上的 `grep` 被认为是二进制文件,因为它有一个长破折号 (0x96) 而不是常规的 ASCII 连字符/减号 (0x2d)。我想这个答案解决了 OP 的问题,但它似乎不完整。 (3认同)
  • BSD grep(在 MacOS 上可用)也支持 `-a` / `--text` (2认同)

小智 156

grep -a 为我工作:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text
Run Code Online (Sandbox Code Playgroud)

  • 这是 IMO 最好、最便宜的答案。 (9认同)
  • 但不符合 POSIX (2认同)
  • 你介意解释一下为什么不是吗?对于我们所有找到此答案作为选项的人来说,明确表示会很好。谢谢 :)。 (2认同)

小智 25

您可以使用strings实用工具从任何文件,然后把它管道的文本内容grep,就像这样:strings file | grep pattern

  • 非常适合 grepping 可能部分损坏的日志文件 (3认同)
  • 是的,有时也会发生二进制混合日志记录。这很好。 (2认同)

Cir*_*郝海东 21

GNU grep 2.24 RTFS

结论:仅2例和2例:

这些检查仅在输入的第 N 个字节之前完成,其中 N = TODO(在一个测试系统中为 32KiB)。如果在第 N 个字节后检查失败,则该文件仍被视为文本文件。(由 Stéphane Chazelas 提及)。

最多只能读取第一个缓冲区

因此,如果在一个非常大的文件中间发生 NUL 或编码错误,无论如何它都可能会被 grepped。

我想这是出于性能原因。

例如:这会打印以下行:

printf '%10000000s\n\x80a' | grep 'a'
Run Code Online (Sandbox Code Playgroud)

但这不会:

printf '%10s\n\x80a' | grep 'a'
Run Code Online (Sandbox Code Playgroud)

实际缓冲区大小取决于读取文件的方式。例如比较:

export LC_CTYPE='en_US.UTF-8'
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'
Run Code Online (Sandbox Code Playgroud)

使用sleep,第一行即使只有 1 个字节长也会传递给 grep,因为进程进入睡眠状态,并且第二次读取不会检查文件是否为二进制文件。

实时文件系统

git clone git://git.savannah.gnu.org/grep.git 
cd grep
git checkout v2.24
Run Code Online (Sandbox Code Playgroud)

查找 stderr 错误消息的编码位置:

git grep 'Binary file'
Run Code Online (Sandbox Code Playgroud)

引导我们/src/grep.c

if (!out_quiet && (encoding_error_output
                    || (0 <= nlines_first_null && nlines_first_null < nlines)))
    {
    printf (_("Binary file %s matches\n"), filename);
Run Code Online (Sandbox Code Playgroud)

如果这些变量命名得很好,我们基本上就得出了结论。

编码错误输出

快速搜索encoding_error_output显示可以修改它的唯一代码路径通过buf_has_encoding_errors

clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
  return true;
Run Code Online (Sandbox Code Playgroud)

然后只是man mbrlen

nlines_first_null 和 nlines

初始化为:

intmax_t nlines_first_null = -1;
nlines = 0;
Run Code Online (Sandbox Code Playgroud)

所以当发现空值时0 <= nlines_first_null变为真。

TODO 什么时候可以nlines_first_null < nlines是假的?我变懒了。

POSIX

不定义二元选项grep - 在文件中搜索模式 | pubs.opengroup.org和 GNU grep 没有记录它,所以 RTFS 是唯一的方法。

  • 请注意,检查有效的 UTF-8 只发生在 UTF-8 语言环境中。另请注意,检查仅在从文件读取的第一个缓冲区上完成,对于常规文件,在我的系统上似乎是 32768 字节,但对于管道或套接字,可以小到一个字节。比较 `(printf '\n\0y') | grep y` with `(printf '\n'; sleep 1; printf '\0y') | 例如 grep y`。 (2认同)
  • 该死的,唯一彻底而准确地解决问题的答案就是坐在这里,获得最多投票者 10% 的选票。 (2认同)
  • @Quasímodo https://cirosantilli.com/stack-overflow#image-stack-overflow-in-a-nutshell (2认同)

zza*_*per 6

我的一个文本文件突然被 grep 视为二进制文件:

$ file foo.txt
foo.txt: ISO-8859 text
Run Code Online (Sandbox Code Playgroud)

解决方案是使用iconv以下方法转换它:

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
Run Code Online (Sandbox Code Playgroud)

  • grep 2.21 将 ISO-8859 文本文件视为二进制文件,在 grep 命令之前添加 export LC_ALL=C。 (4认同)

小智 5

该文件/etc/magic/usr/share/misc/magic具有命令file用于确定文件类型的序列列表。

请注意,二进制可能只是一种后备解决方案。有时具有奇怪编码的文件也被视为二进制文件。

grep在 Linux 上有一些选项来处理二进制文件,如--binary-files-U / --binary