zsh通配符限定符用于排除二进制文件

6
我正在寻找在当前目录及其所有子目录中包含字符串“abc”的文件:
grep abc **/*(.)

输出结果包含以下内容:
...
Binary file test.pdf matches
...

在glob限定符中排除二进制文件是否可能?

编辑:这里使用grep只是一个例子。我感兴趣的是通过zsh globbing限定符排除二进制文件,而不是适当的grep选项。


1
定义“二进制文件”。grep可以识别它们,因为它实际上查看每个文件的内容;这不是一些(大多数?)文件系统跟踪的属性,而zsh只在查找模式匹配时查看文件系统元数据。 - chepner
谢谢。我的意思是包含二进制数据的文件,比如pdf,但不像纯文本文件那样。好吧,仔细想想,所有文件都以某种方式包含二进制数据。看起来zsh globbing无法实现我想做的事情。如果您将您的评论发布为答案,我会接受它。更新:grep -I mc **/*(.)可以实现我想要的功能。 - sieste
1
例如,在Unix上,文本文件和二进制文件之间没有区别;“文本”文件只是将其字节解释为文本的ASCII(或UTF-8或其他)编码的文件。 grep仅根据高位设置的字节比例进行猜测。具有许多非ASCII字母的UTF-8文件也将被解释为二进制文件。 - chepner
2个回答

6
你可以使用glob限定符执行任意代码。在zshexpn(1)中查找estring和+cmd。

不需要任何设置:
ls **/*(.e:'file --mime $REPLY | grep -iqv binary':)

或者让它更不尴尬:
notbinary() { file --mime $REPLY | grep -iqv binary }
ls **/*(.+notbinary)

6

"二进制文件test.pdf匹配"这个消息是由grep本身而不是zsh打印出来的。

原因是大多数情况下,如果您要打印包含模式的二进制文件行,它也会打印出"垃圾"(即非可打印字符、非常长的行等)。

在您的示例中,** / * (.)是zsh扩展。您可以使用echo检查其扩展内容:

$ echo **/*(.)

请注意,**/*(.)无法匹配以点开头的文件在顶级目录中。
$ mkdir test
$ cd test
$ touch .mytest
$ echo  **/*(.)
zsh: no matches found: **/*(.)

如果你想要在当前目录递归地查找包含某种模式的文件,有一种非常简单的方法:

$ grep -rI .

如果您想忽略当前目录中以点开头的文件:

$ grep -r *

关于使用zsh globbing来过滤二进制文件。 这是zshexpn(1)的一部分:

A qualifier may be any one of the following:

   /      directories
   F      `full'  (i.e.  non-empty)  directories.  
   .      plain files
   @      symbolic links
   =      sockets
   (...)

请注意,尽管手册上说“plain files”,但它并不是指“纯文本文件”,而是指普通文件。
据我所知,zsh没有根据文件内容是否为二进制文件来匹配的选项。
在进行globbing时,zsh不会读取文件内容,而是使用可用的文件系统元数据。
因此,如果zsh要实现此功能,则globbing时间将比当前可用的globbing慢得多(除非当然文件系统实现了一种“标记”二进制文件的方法,但我认为这是不太可能的)。
您可以尝试过滤具有执行权限的文件,但这将非常不精确(即可执行脚本会被排除在外,而非可执行二进制文件会被包括在内)。
由于grep本身将读取文件,因此此任务更适合于grep。

我不得不编辑我的问题。我只是举了grep作为例子;我想通过zsh globbing实现排除二进制文件,而不是通过grep选项。这样我就可以在除grep之外的命令中重用“排除二进制”模式。@chepner上面给出了一个很好的解释,为什么这可能是不可能的。如果你能在你的答案中提到这一点,我会接受它。 - sieste
我现在明白你的意思了。 我认为zsh本身没有这个功能。 你需要编写一个脚本。 - diogovk

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