如何在“find”命令中排除所有“permission denied”消息?

943

我需要隐藏所有来自以下信息的权限被拒绝消息:

find . > files_and_folders

我在尝试时遇到这样的消息。我需要收集所有未出现此类消息的文件夹和文件。

是否可以将权限级别指定到 files_and_folders 文件中?

如何同时隐藏错误?


很好的问题!不幸的是,前三个答案在Debian Linux上根本不起作用。或者至少对我的配置不起作用。我需要Fatih的解决方案,find /. -name 'toBeSearched.file' 2>/dev/null - HoldOffHunger
我发现最好使用“-path”选项来排除“/proc”路径。这有助于否定“-prune”选项,以避免打印修剪的项目。 - Charlie Reitzel
21个回答

645

使用:

find . 2>/dev/null > files_and_folders

当然,这将隐藏不仅是Permission denied错误,而是所有错误消息。

如果你真的想保留其他可能出现的错误(例如符号链接上的太多跳转),但不想保留权限被拒绝的错误,那么你可能需要猜测一下你没有许多名为“permission denied”的文件,尝试:

find . 2>&1 | grep -v 'Permission denied' > files_and_folders

如果你只想严格过滤标准错误信息,你可以使用更复杂的构造方式:

find . 2>&1 > files_and_folders | grep -v 'Permission denied' >&2
find 命令的 I/O 重定向是: 2>&1 > files_and_folders |。 管道符号将标准输出重定向到 grep 命令,并首先应用。 2>&1 将标准错误发送到与标准输出相同的位置(即管道)。> files_and_folders 将标准输出(但不是标准错误)发送到文件。最终结果是写入标准错误的消息被发送到管道,而 find 的常规输出被写入文件中。 grep 过滤标准输出(您可以决定要多么有选择性,并根据语言环境和操作系统更改拼写),最后的 >&2 表示幸存的错误消息(写入标准输出)再次发送到标准错误。 最后的重定向符号在终端上可以视为可选项,但在脚本中使用它是一个非常好的主意,因此错误消息显示在标准错误上。 这个主题有无数种变化,具体取决于你想要做什么。这将适用于任何 Unix 变体,带有任何 Bourne shell 派生(Bash、Korn 等)和任何 POSIX 兼容版本的 find。 如果你想要适应你系统上的特定版本的 find,可能会有其他可用选项。GNU 的 find 特别有许多其他版本不具备的选项 - 可以参考当前被接受的答案中的一组选项。

11
如果你和我一样,会注意到空间不足非常重要!请注意,2>/dev/null中没有空格! - Nik
25
2>是一个没有空格的单元,你可以在它和文件名之间加上空格。类似地,在其他重定向中也是如此,比如 2>&1(将标准错误重定向到与标准输出相同的位置),或者 2>&- 关闭标准错误等等。有关其余详细信息,请参见重定向。 (上面的代码是通用的类POSIX shell,不特定于bash。) - Jonathan Leffler
7
这怎么是可接受的解决方案呢?1)你正在将所有错误重定向到dev/null;2)你正在过滤明确的错误字符串!这种做法很容易出问题,如果你的文件在一个名为“permission denied”的目录中怎么办?糟糕了! - Gunchars
2
@Gunchars:你提出的问题在答案中已经有所涉及,或者说明确的错误字符串已经被问题的规范所覆盖。第一个命令将所有错误发送到/dev/null;而第二个命令则没有。答案还提到了假设你没有任何名为“permission denied”的文件。因此,我不清楚你真正反对的是什么。 - Jonathan Leffler
10
我反对使用grep查找错误字符串来修改程序的输出。这种方法在大多数情况下可能有效,但并不是正确的解决方案(使用以下权限查找才是正确的解决方案)。举个例子,这在OSX上将无法工作,因为错误信息是“Permission denied”。在任何其他系统中,即使错误字符串有微小差异(如国际化),也会出现同样的问题。 - Gunchars
显示剩余8条评论

325

注意:

  • 这个答案可能比使用情况需要的更深入,而且在许多情况下 find 2>/dev/null 可能已经足够好了。尽管被保护的情况可能主要是假设性的,但它仍可能对于跨平台视角和讨论一些高级shell技术以寻找尽可能健壮的解决方案是有兴趣的。

如果你的shell是bash或zsh,那么有一个解决方案既健壮又相对简单,只使用符合POSIX标准的find特性;虽然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'才能按预期工作。然而,无论如何,任何显示的错误消息都将是英文。

  • >(...)是一个(很少使用的)输出进程替换,允许将输出重定向(在这种情况下,将stderr输出 (2>) 重定向到 >(...) 内部命令的stdin中。
    除了bashzsh之外,ksh也基本支持它们,但尝试将它们与来自stderr的重定向组合起来,就像这里做的一样(2> >(...)),似乎会被默默地忽略(在ksh 93u+中)。

    • grep -v 'Permission denied'过滤掉(-v)所有包含短语Permission denied的行(来自find命令的stderr流),并将剩余的行输出到stderr (>&2)。

    • 注意:有一小部分可能会在find完成后到达grep的输出,因为整个命令不等待>(...)内部命令完成。在bash中,您可以通过将| cat附加到命令来防止这种情况发生。

这种方法具有以下特点:
  • 鲁棒性:仅将错误消息(而不是文件路径和错误消息的组合,可能导致误报)应用于grep,并将权限被拒绝以外的其他错误消息传递到 stderr。

  • 无副作用:保留了find的退出代码: 无法访问至少一个遇到的文件系统项目会导致退出代码为1(尽管这不会告诉您是否发生了除权限被拒绝的其他错误)。


符合POSIX标准的解决方案:

完全符合POSIX标准的解决方案要么有限制,要么需要额外工作。

如果find的输出无论如何都要被捕获到一个文件(或者完全被抑制),那么基于管道的Jonathan Leffler's answer解决方案简单、健壮且符合POSIX标准:

find . 2>&1 >files_and_folders | grep -v 'Permission denied' >&2

请注意重定向的顺序很重要:2>&1 必须放在最前面。
提前将stdout输出捕获到文件中,可以使2>&1 仅通过管道发送错误消息,这样grep就可以明确地操作。
唯一的缺点是,总体退出代码将是grep命令的代码,而不是find的代码。在这种情况下,如果根本没有错误或只有权限被拒绝的错误,则退出代码将为1(表示失败),否则(除了权限被拒绝的错误之外的其他错误)为0 - 这与意图相反。
话虽如此,find的退出代码很少使用,因为它通常传达的信息很少,仅限于基本故障,例如传递不存在的路径。
然而,即使只有某些输入路径由于缺乏权限而无法访问,也会在find的退出代码中反映出来(在GNU和BSD find中都是如此):如果处理的任何文件出现权限被拒绝的错误,则退出代码将设置为1

以下变化解决了这个问题:

find . 2>&1 >files_and_folders | { grep -v 'Permission denied' >&2; [ $? -eq 1 ]; }

现在,退出代码表示是否发生了除“权限被拒绝”之外的任何错误:1表示有错误,0表示没有错误。
换句话说:如果根本没有错误或仅出现权限被拒绝错误,则现在退出码反映了命令的真正意图:成功(0)报告。
这甚至可以比在顶部的解决方案中仅传递find的退出代码更好。

gniourf_gniourf在评论中提出了一个仍然符合POSIX的通用解决方案,使用复杂的重定向,即使打印文件路径到stdout的默认行为也可以使用:

{ find . 3>&2 2>&1 1>&3 | grep -v 'Permission denied' >&3; } 3>&2 2>&1

简而言之:使用自定义文件描述符3来临时交换stdout(1)和stderr(2),以便仅将错误消息通过stdout传输到grep。如果没有这些重定向,数据(文件路径)和错误消息都将通过stdout传输到grep,grep将无法区分“Permission denied”错误消息和(假设的)文件名称恰好包含短语“Permission denied”的情况。与第一个解决方案一样,报告的退出代码将是grep的而不是find的,但可以应用上述相同的修复方法。

现有答案的注释:

  • 关于 Michael Brux's answer, find . ! -readable -prune -o -print,有几点需要注意:

    • 它需要使用 GNUfind;值得注意的是,它在 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 answersudo find . > files_and_folders简洁而实用,但除了仅仅打印文件名之外,不建议使用该命令,出于安全原因:因为你正在以root用户的身份运行,所以“你面临一个风险,即由于find中的漏洞或恶意版本、不正确的调用或写入意外内容而使整个系统被破坏,如果以正常特权运行,则不可能发生这种情况”(来自 tripleee 在 mist's answer 的评论中)。

  • viraptor's answer 中的第二个解决方案 find . 2>&1 | grep -v 'Permission denied' > some_file 存在误报的风险(因为将混合的 stdout 和 stderr 通过管道发送),并且可能会将非权限拒绝错误通过 stderr 报告,同时将它们与输出路径一起捕获到输出文件中。


5
为什么要使用进程替换而不是管道:find . 2>&1 > files_and_folders | grep -v 'Permission denied' >&2 - gniourf_gniourf
3
如果你不想将输出写到外部文件,可以使用更多的管道操作来实现:{ find . 3>&2 2>&1 1>&3 | grep -v 'Permission denied' >&3; } 3>&2 2>&1。这个命令会在当前目录下查找文件,并过滤掉权限被拒绝的文件,最后将结果输出到标准输出(屏幕)。 - gniourf_gniourf
3
它的意思是它符合POSIX标准,但进程替代 >(...) 是特定于Bash的。 - gniourf_gniourf
3
我不确定是否应该强调和宣传“find”命令的退出代码的保留:众所周知,“find”命令的退出代码几乎没有什么用处。在这里,它很可能是非零的(并且没有什么用处)。 - gniourf_gniourf
3
POSIX明确要求具备执行/搜索文件模式权限才能够“搜索”目录(检索所包含文件的inode)。为了进入子目录(除需要读取权限以列出目录中的文件外),find命令也需要这样做。这不是一个“错误”或“移植错误”。 - wjordan
显示剩余5条评论

310

使用:

find . ! -readable -prune -o -print

或更加普遍地说

find <paths> ! -readable -prune -o <other conditions like -name> -print
  • 避免 "Permission denied"
  • 同时不要抑制(其他)错误信息
  • 并获得退出状态 0("所有文件都已成功处理")

适用于:find(GNU findutils)4.4.2。

  • -readable测试匹配可读取的文件。当测试结果为false时,!操作符返回true。而! -readable则匹配不可读取的目录和文件。
  • -prune操作不会进入目录。
  • ! -readable -prune可以翻译为:如果目录不可读,则不进入它。
  • -readable测试考虑到访问控制列表和其他权限因素,而-perm测试则忽略它们。

有关更多详细信息,请参见find(1)手册页


6
已经提到了区别。如果你不理解,那么答案对你可能没有任何区别?STDOUT相同 - STDERR不同(使用此答案会得到其他错误消息) - $?不同(当没有其他错误发生时,为0表示“成功”,而总是> 0表示“不成功”当重定向到dev/null时) - 或许某人需要在脚本中“正确”的$? - Michael Brux
6
最明显的缺陷是Jonathan的答案(grep -v)将排除文件名包含“Permission denied”的文件 :) - 林果皞
67
我认为在这里需要补充一点,如果你需要添加其他的搜索条件,应该使用 -ofind . ! -readable -prune -o -name '*.txt' - tempestadept
26
请注意,POSIX的find命令不包括-readable选项;同样,BSD的find命令以及Mac OS X的find命令也不包括该选项(我不确定其他系统是否有此选项)。因此,如果您无法保证系统安装了GNU find命令,则不太明显如何进行调整。在拥有GNU find可用的Linux系统中,该命令非常有效,而在其他系统上则可能会有所不同。 - Jonathan Leffler
7
find . ! -readable -prune -o -name '*.txt' 在使用 find 4.2.2 进行 Ubuntu 14.04 上似乎无法正常工作。它似乎忽略了 -name。出于某些奇怪的原因,find . \( ! -readable -prune \) -o -name '*.txt' -print 可以成功运行。 - con-f-use
显示剩余13条评论

123

如果您希望从根目录"/"开始搜索,您可能会看到类似以下的输出:

find: /./proc/1731/fdinfo: Permission denied
find: /./proc/2032/task/2032/fd: Permission denied

这是因为权限问题。要解决这个问题:

  1. 你可以使用sudo命令:

    sudo find /. -name 'toBeSearched.file'
    

该命令会要求输入超级用户密码,当你输入密码后你将看到想要的结果。如果你没有使用sudo命令的权限,这意味着你没有超级用户的密码,首先向系统管理员请求将你添加到sudoers文件中。

  1. 你可以使用重定向标准错误输出来避免在屏幕上看到错误信息!将其重定向到特殊文件/dev/null:

find /. -name 'toBeSearched.file' 2>/dev/null
你可以将标准错误输出重定向到标准输出(通常为显示/屏幕),然后使用grep命令并带有-v“invert”参数进行管道操作,以不显示具有“Permission denied”单词对的输出行。
find /. -name 'toBeSearched.file' 2>&1 | grep -v 'Permission denied'

8
除了它没有回答问题之外,建议向系统管理员请求将您添加到sudoers文件中,然后执行“sudo find...”命令。 - Stephen
4
正是我在寻找的! - DankMasterDan
1
自从我读了四篇高票、一页长的错误答案后,终于找到了正确答案。谢谢! - HoldOffHunger

107

我不得不使用:

find / -name expect 2>/dev/null

我指定了要查找的名称,然后告诉它将所有错误重定向到/dev/null。

期望是我正在搜索的expect程序的位置。


3
@Masi,答案中的命令没有使用expect。相反,expect只是此命令将尝试查找的文件的名称。 - Dhruv Kapoor
2
盲目地重定向所有stderr输出,只是为了忽略单个类别的错误消息通常是一个坏主意 - 在这个过程中,您将失去所有其他任意的错误。 - Josip Rodin

71

使用2>/dev/nullstderr导向到/dev/null

find . -name '...' 2>/dev/null


3
即使在Mac OSX上,这对我来说也很好用。或者使用 find . -name '...' -print 2>/dev/null - shadowsheep

31

5
-perm -g+r,u+r,o+r仅匹配对于文件的所有3个安全主体设置了r(读取)权限的文件,这与当前用户是否能够读取该文件没有直接关系。它有可能会错过当前用户可以读取的文件,并匹配他们无法读取的文件。 - mklement0
我认为 find . -type d ! \( -perm -u+r -o -perm -g+r -o -perm -o+r \) -prune -o -print 是一个好的解决方案。 - Mattia72
2
@Mattia72:不,使用“-perm”完全模拟“-readable”是根本不可能的——请参考我的先前评论并考虑以下示例:echo 'hi' > file; sudo chown nobody:nobody file; sudo chmod o-r file; find file -perm -u=r会打印出file,因为它的用户读取位被设置了,但它与当前用户无关。当前用户无法读取此文件;尝试cat file。另请参见:我的这个答案 - mklement0

23

重定向标准错误。例如,在Unix机器上使用bash时,您可以将标准错误重定向到/dev/null,如下所示:

find . 2>/dev/null >files_and_folders

20

尽管上述方法不能解决Mac OS X的情况,因为Mac OS X不支持-readable开关,但以下是您可以避免输出中出现“Permission denied”错误的方式。这可能对某些人有所帮助。

find / -type f -name "your_pattern" 2>/dev/null

如果您正在使用其他一些与find相关的命令,例如在目录中查找某种模式的文件大小,则2>/dev/null仍将像下面所示一样起作用。

find . -type f -name "your_pattern" -exec du -ch {} + 2>/dev/null | grep total$

这将返回给定模式文件的总大小。请注意,在find命令结尾处的2>/dev/null


很好的绑定到了OS X系统!Jonathan的回答解释了2>/dev/null这一部分。你可以解释一下-exec du -ch {} + 2>/dev/null | grep total$这一部分吗? - Léo Léopold Hertz 준영
1
@Masi,您可以使用-exec选项与任何命令一起使用,以对find命令找到的文件或目录采取进一步的操作。 du -ch file_pattern计算匹配file_pattern的每个文件的大小,并且该输出的最后一行是匹配file_pattern的所有文件的总和。请参阅du的man页面。 grep total只过滤提取总和的行(即最后一行)。 - Bunti

13

这些错误信息被打印到标准错误输出流(fd 2)中。要过滤掉它们,只需将所有错误重定向到/dev/null:

find . 2>/dev/null > some_file

或者先将stderr和stdout合并,然后再使用grep过滤出那些特定的错误信息:

find . 2>&1 | grep -v 'Permission denied' > some_file

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接