事先:这从一个一般问题(我认为每个文件夹都是如此)演变为这个特定的极端情况。
因为似乎还有一些人和我一样好奇,所以我决定不删除这个问题而是重新做一下。
假设我想列出直接包含在我的主文件夹中的所有项目(点文件和文件夹除外)。恕我直言,以下 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
我知道我可以使用-perm
atfind
并2> /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
/-perm
和2> /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)