为什么 "grep foo bar" 打印 "grep: bar: Is a directory" 而不是在 bar/ 中打印与模式 "foo" 匹配的任何文件名?

Chr*_*nry 0 grep directory read

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
Run Code Online (Sandbox Code Playgroud)

如果是这种情况,那么grep -H foo bar将产生输出

bar: foobar/
Run Code Online (Sandbox Code Playgroud)

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

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

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

ETA2 (2021-07-23):这个问题的主要动机不是迫切需要获得所描述的直觉行为(尽管我对此感兴趣,因为它是潜在的次要好处)。动机是为了理解为什么 (GNU)grep根据其输出,其行为方式与其手册页中的声明相矛盾。

答案原来是它grep实际上只是按照其手册页所说的那样做,但是系统调用的(典型)行为的变化read使得在大多数现代系统上的结果与人们推断的结果大不相同仅基于对grep手册页的阅读(不熟悉现代read实现的行为。

虽然总体上read我更愿意那样做,但我很怀疑这种行为与其手册页相矛盾。鉴于目前的情况,我希望在grep手册页中添加一两行,但这并没有,只是误导。

Gil*_*il' 5

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

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

$ grep -H foo bar
Binary file bar matches
$ grep -H --text foo bar
bar:?"!
       .?
..?"!foobar?"!
              baz?"!qux
Run Code Online (Sandbox Code Playgroud)

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

$ rmdir bar/foobar
$ grep -H --text foo bar 
bar:?"!
..?"!foobar?"!
              baz?"!foo
Run Code Online (Sandbox Code Playgroud)

删除目录将其从文件系统中删除,但它并没有从对目录进行编码的磁盘结构中擦除已删除条目的名称。

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

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

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