为什么“grep foo bar”打印“grep:bar:是一个目录”而不是打印 bar/ 中与模式“foo”匹配的任何文件名?

为什么“grep foo bar”打印“grep:bar:是一个目录”而不是打印 bar/ 中与模式“foo”匹配的任何文件名?

man该选项的页面描述grep如下-d ACTION

如果输入文件是目录,则使用ACTION来处理它。默认情况下,ACTIONis read,即读取目录就像读取普通文件一样。 [...]

直观上,我希望这意味着目录bar被视为(出于grepping 目的)相当于一个文本文件,其中包含或多或少vim与我键入时显示的内容大致相同的内容vim foo,即粗略的内容(取决于变化是什么)某种解释性信息和/或元数据位于顶部和底部),例如:

"============================================================================
" Netrw Directory Listing                                        (netrw v156)
"   /home/chris-henry/bar
"   Sorted by      name
"   Sort sequence: [\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$
"   Quick Help: <F1>:help  -:go up dir  D:delete  R:rename  s:sort-by  x:special
" ==============================================================================
../
./
foobar/
baz/
qux

如果是这种情况,grep -H foo bar则会产生输出

bar: foobar/

相反,它给出了消息grep: bar: Is a directory。为什么是这样?是否有任何(相当简单的)方法来获得直观的结果(不仅是这个简单的搜索,而且还包括诸如grep foo *where*可能匹配任何或所有文本文件、二进制文件和目录之类的搜索)?

预计到达时间 (2021-07-22):正如已接受的答案所建议的并在评论中确认的那样,grep foo bar它本身实际上完全符合我的预期:它使用文件描述符 for调用系统调用read( ) ,就像普通文件一样。当 时,不是填充的内容,而是返回错误代码,打印适当的诊断消息,然后继续处理下一个文件 - 就像返回错误代码(有时不是 或)并且是普通文件。ssize_t read(int fd, void *buf, size_t count)barbarread*bufbarEISDIRgrepreadEINTREINVALbar

我的期望和现实之间的差异来自于 Linux 版本(以及从评论来看,大多数其他现代版本)的行为read,即当fd引用目录时,它会自动返回EISDIR

预计到达时间2 (2021-07-23):这个问题的主要动机并不是迫切需要获得所描述的直观行为(尽管我对此作为潜在的次要好处感兴趣)。动机是理解为什么(GNU)grep根据其输出,其行为方式似乎与其手册页中的声明相矛盾。

答案是,grep实际上正在按照其手册页所述进行操作,但是对系统调用的(典型)行为的更改read使得结果其中,在大多数现代系统上,这与仅根据阅读手册grep页(不熟悉现代read实现的行为)所推断的内容有很大不同。

虽然总的来说,我确实宁愿read不这样做,但我相当怀疑这种行为是否与它是手册页。鉴于目前的情况,我希望看到在grep手册页中添加一两行,但事实并非如此错误的事实上,只是误导。

答案1

目录没有文本形式的内在表示。许多 Unix 变体允许程序像读取常规文件一样从目录中读取数据,但这几乎没有用,因为内容的格式取决于文件系统。一些现代 Unix 变体,包括 Linux,完全阻止程序读取目录,就好像它是常规文件一样

例如,下面是 FreeBSD 上发生的情况(旧版本仍然允许它 - 从 FreeBSD 13 开始,默认情况下禁用它),目录如下bar

$ grep -H foo bar
Binary file bar matches
$ grep -H --text foo bar
bar:�"!
       .�
..�"!foobar�"!
              baz�"!qux

是的,您可以确定它foo存在于目录表示中,但您不能确定它是否是文件名的一部分。例如(仍然在那台 FreeBSD 机器上):

$ rmdir bar/foobar
$ grep -H --text foo bar 
bar:�"!
..�"!foobar�"!
              baz�"!foo

删除目录会将其从文件系统中删除,但不会从编码该目录的磁盘结构中擦除已删除条目的名称。

当您要求 Vim 打开一个目录时,Vim 会遍历该目录(使用专用系统函数,例如readdir,而不是使用通用read函数)并以良好的方式显示结果。

Grep 可以实现类似的东西,但是相对于 grep 的大小来说,这将是大量的工作,它会偏离 grep 的核心目的,即搜索文件的内容,并且实现必须是一种折衷方案,不不能满足很多人。目录的文本表示形式是否仅包含文件名或一些元数据(为什么找不到grep "Jul 20" bar7 月 20 日修改的文件)?如何分隔条目(如果它们由换行符分隔,则表示形式不明确,因为文件名可以包含换行符;如果它们由空字节分隔,则输出仅对 有用grep --null-data)?

为了搜索文件名,已经有更好的工具,例如 shell 通配符findlocate.

相关内容