尽管有`-prune`,为什么`find` 仍然尝试访问.gvfs 甚至将其包含在输出中?

DJC*_*mmy 5 shell find

事先:这从一个一般问题(我认为每个文件夹都是这种情况)演变为这个特定的极端情况。
由于其他一些人似乎和我一样好奇,我决定不删除这个问题而是重新编写它。


假设我想列出我的主文件夹中直接包含的所有项目,点文件​​和文件夹除外。恕我直言,下面的 find 命令应该这样做(我明确地写了“点项目”,因为如果你使用一个.hidden文件,“隐藏项目”可能会有所不同)

find $HOME/ -mindepth 1 -maxdepth 1 -path "$HOME/.*" -prune -o -print
Run Code Online (Sandbox Code Playgroud)

-mindepth 1确保不包含 $HOME 本身,-maxdepth 1限制为一个文件夹级别,-path "$HOME/.*" -prune排除主文件夹中以点开头的所有内容,并-o -print根据手册页确保输出中没有排除的元素。

到现在为止还挺好。它有点工作(即使存在您没有权限的点文件夹),它开始窒息,$HOME/.gvfs尽管由于以下原因根本不应该访问它-path ... -prune

/home/user/file a
/home/user/folder b
...
find: '/home/user/.gvfs': Permission denied
/home/user/.gvfs
...
/home/user/file z

Run Code Online (Sandbox Code Playgroud)

我知道我可以使用-permatfind2> /dev/null解决它,但真正让我震惊的是,它/home/user/.gvfs包含在列表中(尽管所有其他点项都被排除在外)!!!
而且我很确定一些不太小心的人很想摆脱错误2> /dev/null并继续前进。...如果他们完全识别错误,因为在包含 100 行的较长列表中,您必须1> /dev/null在使用该命令之前使用添加等方式明确检查错误。


真的忽略目录/树?

为什么要find尝试访问/home/user/.gvfs,尽管-maxdepth 1或至少-prune应该阻止这种情况?例如从手册页:

'-prune' 动作(只会阻止进一步下降[...])

即使在较旧的手册页(安装在我的系统上)中-path也指出:

忽略整个目录树,请使用 -prune 而不是检查树中的每个文件。

“被忽略”目录本身的危险清单!

如果所有/home/user/.*这些都真的被忽略了,也许我的问题的第二部分就没有必要了,因为在我看来这是因为一个内部错误:
为什么/home/user/.gvfs仍然在列表中以及如何防止它?有没有办法不用摆弄-user/-perm2> /dev/null每个find命令(至少有一个-prune动作)?

未来用途 -> 效率?

-prune(或至少用-path ... -prune)真正忽略了什么?

如果-prune仍然(几乎)所有文件都被检查,我会放弃使用这个复杂的选项,而是使用!or -not,特别是因为-prune显然更容易出错。

Sté*_*las 6

这似乎是一个错误在GNU实现的find。在这里,触发器似乎是这样一个事实,即lstat()/fstatat()系统调用(用于检索文件的元数据)在该文件上失败EACCESS权限被拒绝.gvfs

对于作为基于 FUSE 的文件系统的挂载点的文件,在 Linux 上通常会发生这种情况,而该-o allow-other选项尚未用于该文件系统。当存在指向不可访问区域的符号链接时,find -L/也可能发生这种情况find -follow

我在这里发现-prune这些文件返回false,这显然是一个错误,正如-prune所记录的(并且是 POSIX 要求的)总是返回true

作为解决该错误之前的解决方法,您可以替换-prune'(' -prune -o -true ')'(请注意,这-true是一个 GNU 扩展;一个可移植的等效项可以! -name ''保证是正确的,尽管您也可以这样做'(' -prune -o ! -prune ')'

现在,您的代码本身存在一些问题。在:

find $HOME/ -mindepth 1 -maxdepth 1 -path "$HOME/.*" -prune -o -print
Run Code Online (Sandbox Code Playgroud)
  • 在大多数类似 Bourne 的 shell 中,$HOME/应该引用它,否则$HOME扩展会受到 split+glob 的影响。你也可以写出来~/
  • -prune是告诉find不要进入目录。在这里它是多余的,因为maxdepth 1它将停止find下降到任何目录(除了顶级(深度 0)一个$HOME/)。
  • -path需要一个模式。这意味着 的内容$HOME将被视为一种模式。$HOME例如/home/[112] me,如果您是,-path '/home/[112] me/.*'将匹配 on /home/2 me/.foo,但不匹配on /home/[112] me/.foo。此处也不需要匹配完整路径,您可以匹配文件名而不是-name '.*'.
  • *with GNUfind只匹配characters,因此.*匹配以 开头的文件名,. 并且也由形成有效字符的字节序列组成。因此,您只能使用它来匹配语言环境中的隐藏文件,其中每个字节序列都形成有效字符(例如C语言环境)。

所以在这里,如果重点是列出 HOME 目录中的非隐藏文件及其完整路径,你会这样做:

LC_ALL=C find ~/ -mindepth 1 -maxdepth 1 ! -name '.*'
Run Code Online (Sandbox Code Playgroud)

这也将避免 GNUfind错误。

或标准等价物(尽管/./在路径中添加了 a ):

LC_ALL=C find ~/. ! -name . -prune ! -name '.*'
Run Code Online (Sandbox Code Playgroud)

(这里,GNUfind错误会因文件不以开头.不可用而受到攻击lstat(),但您将在上面的错误描述中看到,是否lstat()报告不可用文件会因实现而有很大差异)。

在这里,我只使用zsh's:

print -rC1 ~/*(ND)
Run Code Online (Sandbox Code Playgroud)

或者,如果您不希望它们按以下方式排序find

print -rC1 ~/*(NDoN)
Run Code Online (Sandbox Code Playgroud)