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

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

事先:这从一个一般问题(我认为每个文件夹都是如此)演变为这个特定的极端情况。
因为似乎还有一些人和我一样好奇,所以我决定不删除这个问题而是重新做一下。


假设我想列出直接包含在我的主文件夹中的所有项目(点文件和文件夹除外)。恕我直言,以下 find 命令应该执行此操作(我明确写了“点项目”,因为如果您使用文件,.hidden“隐藏项目”可能会有所不同):

find $HOME/ -mindepth 1 -maxdepth 1 -path "$HOME/.*" -prune -o -print

-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

我知道我可以使用-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它显然更容易出错。

答案1

这似乎是一个错误在 GNU 实现中find.在这里,触发器似乎是lstat()/fstatat()系统调用(检索文件的元数据)失败并显示EACCESS没有权限)在该.gvfs文件上。

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

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

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

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

find $HOME/ -mindepth 1 -maxdepth 1 -path "$HOME/.*" -prune -o -print
  • 在大多数类似 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 '.*'.
  • *仅与 GNUfind匹配人物,因此.*匹配以以下开头的文件名. 也由形成有效字符的字节序列组成。因此,您只能使用它来匹配每个字节序列形成有效字符(例如C区域设置)的区域设置中的隐藏文件。

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

LC_ALL=C find ~/ -mindepth 1 -maxdepth 1 ! -name '.*'

这也可以避免 GNUfind错误。

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

LC_ALL=C find ~/. ! -name . -prune ! -name '.*'

(在这里,GNUfind错误将被击中的文件不是开始于.和 are not lstat()possible,但是您会在上面的错误描述中看到,是否lstat()报告 non-able 文件随实现的不同而有很大差异)。

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

print -rC1 ~/*(ND)

或者,如果您不希望它们像这样排序find

print -rC1 ~/*(NDoN)

相关内容