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

我注意到文件中包含一些ascii NUL字符,我相信这是数据库转储中的工件。

那么,是什么让grep认为这些文件是二进制文件? NUL的字符?文件系统上是否有标志?要使grep向我显示行匹配项,我需要更改什么?

评论

如果NUL是分隔符,则--null-data可能很有用。

#1 楼

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

可能存在类似cat file | tr -d 'q4312079q0' | yourgrep这样的变通办法,首先消除所有空值,然后搜索文件。

评论


...或至少在GNU grep中使用-a /-text。

–德罗伯特
2012年11月26日20:44

@derobert:实际上,在某些(较旧的)系统上,grep看到了行,但是它的输出会在第一个NUL处截断每个匹配的行(可能是因为它调用了C的printf并给了它匹配的行?)。在这样的系统上,grep cmd .sh_history将返回与匹配“ cmd”的行一样多的空行,因为sh_history的每一行都有特定的格式,每行的开头都有NUL。 (但是您的评论“至少在GNU grep上可能是对的。我现在没有人可以测试,但我希望他们能很好地处理此问题)”

–奥利维尔·杜拉克(Olivier Dulac)
13年11月25日在11:46



是否存在NUL字符是唯一标准?我对此表示怀疑。它可能比这更聪明。任何超出Ascii 32-126范围的东西都是我的猜测,但是我们必须查看源代码才能确定。

–迈克尔·马丁内斯(Michael Martinez)
15年8月14日在16:58

我的信息来自特定grep实例的手册页。您对实施的评论是有效的,来源优于docs。

–bbaja42
15年8月18日在22:31

我有一个文件,其在cygwin上的grep被认为是二进制文件,因为它的长破折号(0x96)而不是常规的ASCII连字符/减号(0x2d)。我猜这个答案解决了OP的问题,但看来它是不完整的。

–cp.engr
16年2月15日在16:15

#2 楼

grep -a为我工作:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text


评论


这是IMO最好,最便宜的答案。

–pydsigner
16-09-24在18:32

但不符合POSIX

–马特奥
19年8月9日在8:08

您介意解释为什么不是吗?对于我们谁都可以将此答案作为选择的所有人来说,最好弄清楚一点。谢谢 :)。

– ivanleoncz
19年11月4日在19:36

嘿,我来这里是第二次重新学习这个LOL。文本中的法语口音(变音符号)使grep变成了barf

– zzapper
20-10-26在9:43

#3 楼

您可以使用strings实用程序从任何文件中提取文本内容,然后通过grep将其通过管道传输,如下所示:strings file | grep pattern

评论


grep处理可能部分损坏的日志文件的理想选择

–汉斯·R。
2015年2月27日在7:43

是的,有时还会发生二进制混合日志记录。很好

–sdkks
17年3月3日在16:59

#4 楼

GNU grep 2.24 RTFS

结论:仅2种和2种情况:


NUL,例如根据C99 printf 'ambrlen()' | grep 'a'的编码错误,例如:

export LC_CTYPE='en_US.UTF-8'
printf 'a\x80' | grep 'a'


,因为\x80不能是UTF-8 Unicode点的第一个字节:UTF-8-说明| en.wikipedia.org


此外,正如StéphaneChazelas所提到的,是什么使grep认为文件是二进制文件? |在Unix&Linux Stack Exchange中,仅在读取长度为TODO的第一个缓冲区之前进行这些检查。

仅在读取第一个缓冲区之前进行

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

我想这是出于性能原因。
例如:这将打印行:

printf '%10000000s\n\x80a' | grep 'a'


但这不是:

printf '%10s\n\x80a' | grep 'a'


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

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


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

RTFS

git clone git://git.savannah.gnu.org/grep.git 
cd grep
git checkout v2.24


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

git grep 'Binary file'


使我们进入/src/grep.c

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


如果这些变量的名称正确,我们基本上可以得出结论。

encoding_error_output

encoding_error_output的快速grepping显示,唯一可以对其进行修改的代码路径通过buf_has_encoding_errors

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


然后仅man mbrlen

nlines_first_null和nlines

初始化为:

intmax_t nlines_first_null = -1;
nlines = 0;


因此,当找到空值时,0 <= nlines_first_null变为true。

TODO nlines_first_null < nlines何时会为假?我很懒

POSIX

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

评论


令人印象深刻的说明!

–user394
16年4月13日在2:02

请注意,仅在UTF-8语言环境中检查有效的UTF-8。还要注意,检查仅在从文件读取的第一个缓冲区上进行,对于我的系统,该文件对于常规文件似乎是32768字节,但对于管道或套接字,则可以小到一个字节。比较(printf'\ n \ 0y')| grep y与(printf'\ n'; sleep 1; printf'\ 0y')|例如grep y。

–StéphaneChazelas
16-4-13在12:18



@StéphaneChazelas“请注意,仅在UTF-8语言环境中才检查有效的UTF-8”:您的意思是关于导出LC_CTYPE ='en_US.UTF-8'的示例,还是其他的? Buf阅读:令人惊叹的示例,添加到答案中。您显然比我更多地阅读了源,让我想起了那些“学生开悟”的黑客koans :-)

– Ciro Santilli郝海东冠状病六四事件法轮功
16年4月13日在13:05

我也没有研究细节,但是最近

–StéphaneChazelas
16年4月13日在13:09

@CiroSantilli巴拿马文件六四事件法轮功您测试了哪个版本的GNU grep?

– jrw32982
16年6月8日在23:33

#5 楼

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

$ file foo.txt
foo.txt: ISO-8859 text


解决方案是使用iconv将其转换:

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt


评论


这也发生在我身上。特别是,原因是ISO-8859-1编码的不间断空格,为了使grep在文件中进行搜索,我必须将其替换为常规空格。

– Gallaecio
2015年6月9日13:50

grep 2.21将ISO-8859文本文件视为二进制文件,请在grep命令之前添加export LC_ALL = C。

– netawater
2015年8月17日在2:52



@netawater谢谢!这是例如如果文本文件中包含类似Müller的内容,则会出现这种情况。这是十六进制的0xFC,因此超出grep预期的范围是utf8(最高0x7F)。检查printf'a \ x7F'|如Ciro所描述的grep'a'。

–Anne van Rossum
16年11月26日在16:51

#6 楼

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

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

Linux上的grep有一些选项可以处理二进制文件,例如--binary-files-U / --binary

评论


更准确地说,根据C99的mbrlen()编码错误。示例和源代码解释位于:unix.stackexchange.com/a/276028/32558

– Ciro Santilli郝海东冠状病六四事件法轮功
16年4月12日在20:51

#7 楼

我的一个学生有这个问题。 grep中的Cygwin中存在错误。如果文件包含非Ascii字符,则grepegrep会将其视为二进制文件。

评论


这听起来像一个功能,而不是一个错误。特别是考虑到有命令行选项来控制它(-a / --text)

–威尔·谢泼德(Will Sheppard)
18年1月29日,11:39



#8 楼

实际回答“是什么使grep认为文件是二进制文件?”的问题,您可以使用iconv

$ iconv < myfile.java
iconv: (stdin):267:70: cannot convert


在我的情况下,西班牙语字符显示正确在文本编辑器中,但grep认为它们是二进制的; iconv输出将我指向这些字符的行号和列号

对于NUL字符,iconv会认为它们是正常的,并且不会打印此类输出,因此该方法不适合

#9 楼

我有同样的问题。我用vi -b [filename]查看添加的字符。我找到了控制字符^@^M。然后在vi中键入:1,$s/^@//g删除^@字符。对^M重复此命令。

警告:要获取“蓝色”控制字符,请按Ctrl + v,然后按Ctrl + M或Ctrl + @。然后保存并退出vi。

#10 楼

我也遇到了这个问题,但就我而言,这是当匹配的行太长时引起的。
file myfile.txt
myfile.txt: UTF-8 Unicode text, with very long lines

grep可以在整个文件中运行很多模式,但是当模式匹配“很长的行”时“它以Binary file myfile.txt matches停止。
添加-a也可以解决此问题,但预先为文件准备NULL或其他无效字符将没有任何效果(没有其他方式的grep无法完成)。在这种情况下,冒犯的行有25k +个字符!
我不明白的是为什么它仅在grep试图返回该行而不是在处理其他模式时才发生。