注意:
- 这个答案可能比用例保证的更深入,并且
find 2>/dev/null 在许多情况下可能已经足够好了。从跨平台的角度来看,它可能仍然对一些先进的 shell 技术的讨论感兴趣,以便找到一个尽可能健壮的解决方案,即使所防范的情况可能在很大程度上是假设性的。
如果您的shell 是bash 或zsh,则有一个解决方案既可靠又相当简单,只使用符合POSIX 的@987654333 @特征;虽然bash 本身不是 POSIX 的一部分,但大多数现代 Unix 平台都附带它,这使得该解决方案具有广泛的可移植性:
find . > files_and_folders 2> >(grep -v 'Permission denied' >&2)
注意:
-
如果您的系统配置为显示本地化错误消息,请在下面的 find 调用前加上 LC_ALL=C (LC_ALL=C find ...) 以确保 报告英文消息,以便grep -v 'Permission denied' 按预期工作。但是,确实显示的任何错误消息都将始终以英文显示。
-
>(...) 是一个(很少使用)输出 process substitution,它允许将输出(在这种情况下,stderr 输出 (2>) 重定向到>(...).
里面的命令
除了bash 和zsh,ksh 原则上也支持它们,但尝试将它们与来自stderr 的重定向结合起来,就像这里所做的那样( 2> >(...)),似乎被默默地忽略了(在 ksh 93u+ 中)。
-
grep -v 'Permission denied' 过滤out (-v) 所有包含短语Permission denied 的行(来自find 命令的stderr 流)并将剩余的行输出到stderr (>&2 )。
-
注意:grep 的某些输出可能会在find 完成后到达,因为整个命令不会等待>(...) 内的命令完成.在bash 中,您可以通过将| cat 附加到命令来防止这种情况发生。
这种方法是:
符合 POSIX 的解决方案:
完全符合 POSIX 的解决方案要么有限制,要么需要额外的工作。
如果find 的输出无论如何要在文件 中捕获(或完全抑制),那么Jonathan Leffler's answer 的基于管道的解决方案很简单、健壮且符合 POSIX:
find . 2>&1 >files_and_folders | grep -v 'Permission denied' >&2
注意重定向的顺序很重要:2>&1 必须在前。
在文件中预先捕获标准输出允许2>&1 通过管道发送仅错误消息,然后grep 可以明确地对其进行操作。
唯一的缺点是整个退出代码将是 grep 命令的,而不是 find 的,在这种情况下意味着:如果有没有个错误或只有个权限被拒绝的错误,退出代码将是1(信号失败),否则(错误不是权限被拒绝的那些)0 - 这与意图相反。
也就是说,find 的退出代码无论如何都很少使用,因为它通常传达的信息很少em>根本性故障,例如通过不存在的路径。
但是,由于缺少权限,甚至只有 一些 输入路径无法访问的特定情况 反映在 find 的退出代码中(在 GNU 和 BSD 中) find):如果处理的任何文件发生权限被拒绝错误,则退出代码设置为1。
以下变体解决了以下问题:
find . 2>&1 >files_and_folders | { grep -v 'Permission denied' >&2; [ $? -eq 1 ]; }
现在,退出代码指示是否有任何错误除了 Permission denied 发生:1 如果是,0 否则。
换句话说:退出代码现在反映了命令的真实意图:如果完全没有错误或仅发生权限被拒绝的错误,则会报告成功 (0)。
可以说,这甚至比仅传递 find 的退出代码更好,就像顶部的解决方案一样。
gniourf_gniourf 在 cmets 中提出了一个(仍然符合 POSIX)使用复杂重定向对这个解决方案进行概括,它即使在将文件路径打印到 标准输出:
{ find . 3>&2 2>&1 1>&3 | grep -v 'Permission denied' >&3; } 3>&2 2>&1
简而言之:自定义文件描述符3 用于临时交换stdout (1) 和stderr (2),这样错误消息单独 可以通过管道传送到grep通过标准输出。
如果没有这些重定向,数据(文件路径)和错误消息都将通过标准输出传送到grep,然后grep将无法区分错误消息 Permission denied 和一个(假设的)文件,其名称恰好包含短语 Permission denied。
然而,与第一个解决方案一样,报告的退出代码将是 grep,而不是 find,但可以应用与上述相同的修复。
现有答案的注释:
-
Michael Brux's answer、find . ! -readable -prune -o -print有几点需要注意:
-
它需要 GNU find;值得注意的是,它不适用于 macOS。当然,如果您只需要使用 GNU find 的命令,这对您来说不是问题。
-
一些Permission denied 错误可能仍然浮出水面:find ! -readable -prune 报告当前用户确实拥有r 权限的目录的子 项的此类错误,但缺少x(可执行)权限。原因是因为目录本身是可读的,-prune 没有被执行,然后尝试下降进入那个目录会触发错误消息。也就是说,典型情况是缺少r 权限。
-
注意:以下几点是哲学和/或特定用例的问题,您可能会认为它与您无关,并且该命令很适合您的需求,特别是如果只是打印路径就是你所做的一切:
-
如果您将权限拒绝错误消息的过滤概念化为单独的任务,您希望能够将其应用于任何
find命令,那么主动防止权限被拒绝错误的相反方法需要在find命令中引入“噪音”,这也引入了复杂性和逻辑缺陷。
- 例如,对 Michael 的回答(截至撰写本文时)投票最多的评论试图通过包含
-name 过滤器来显示如何扩展命令,如下所示:
find . ! -readable -prune -o -name '*.txt'
然而,这没有按预期工作,因为尾随的-print 操作是必需的(可以在this answer 中找到解释)。这种微妙之处可能会引入错误。
-
Jonathan Leffler's answer、find . 2>/dev/null > files_and_folders 中的第一个解决方案,正如他自己所说,盲目地使所有错误消息静音(解决方法很麻烦,而且不完全健壮,正如他也解释的那样)。 务实地说,但是,它是最简单的解决方案,因为您可能满足于假设所有错误都与权限相关。
-
mist's answer, sudo find . > files_and_folders, 简洁而实用,但出于安全原因,除了打印文件名之外的任何事情都是不明智的:因为你是以 root 用户身份运行,“您可能会冒着整个系统被 find 中的错误或恶意版本或错误调用导致意外写入某些内容的风险,如果您运行此程序就不会发生这种情况具有正常权限”(来自 tripleee 对雾的回答的评论)。
-
viraptor's answer、find . 2>&1 | grep -v 'Permission denied' > some_file 中的第二个解决方案存在误报的风险(由于通过管道发送 stdout 和 stderr 的混合),并且可能不会报告非 -permission-denied 错误通过 stderr,将它们与输出文件中的输出路径一起捕获。