关于bash:如何从“find”中排除所有“权限被拒绝”的消息?

How can I exclude all “permission denied” messages from “find”?

我需要隐藏所有被拒绝权限的邮件:

1
find . > files_and_folders

我正在试验这种信息何时出现。我需要收集所有没有出现的文件夹和文件。

是否可以将权限级别直接指向files_and_folders文件?

如何同时隐藏错误?


用途:

1
find . 2>/dev/null > files_and_folders

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

如果您真的想保留其他可能的错误,例如在一个符号链接上跳得太多,但不保留被拒绝的权限,那么您可能需要猜测一下,您没有许多名为"拒绝权限"的文件,然后尝试:

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

如果您严格希望只过滤标准错误,可以使用更详细的构造:

1
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过滤标准输出(您可以决定希望它具有多大的选择性,可能需要根据区域设置和O/S更改拼写),最终的>&2意味着剩余的错误消息(写入标准输出)再次进入标准错误。最后的重定向可以在终端上被视为可选的,但最好在脚本中使用它,以便在标准错误上显示错误消息。

这个主题有无数的变化,这取决于你想做什么。这将适用于具有任何BourneShell派生(bash,korn,…)和任何符合POSIX的find版本的Unix的任何变体。

如果您希望适应您的系统上的特定版本的find,可能会有其他选项可用。GNU find尤其有许多其他版本中不可用的选项-请参阅当前接受的一组选项的答案。


用途:

1
find . ! -readable -prune -o -print

或者更一般地说

1
find <paths> ! -readable -prune -o <other conditions like -name> -print
  • 避免"权限被拒绝"
  • 并且不要抑制(其他)错误消息
  • 并获取退出状态0("所有文件处理成功")

使用:查找(GNU findutils)4.4.2。背景:

  • -readable测试与可读文件匹配。当test为false时,!运算符返回true。和! -readable匹配不可读的目录(文件)。
  • -prune操作不会下降到目录中。
  • ! -readable -prune可以翻译为:如果目录不可读,就不要下降到目录中。
  • -readable测试考虑了访问控制列表和-perm测试忽略的其他权限人工制品。

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


注:*这个答案可能比用例许可证更深入,在许多情况下,find 2>/dev/null可能足够好。对于跨平台的观点以及对一些高级shell技术的讨论,它可能仍然感兴趣,以便找到尽可能强大的解决方案,尽管防范的案例可能主要是假设的。*如果系统配置为显示本地化错误消息,请在下面的find调用前面加上LC_ALL=C(LC_ALL=C find ...以确保报告英语消息,以便grep -v 'Permission denied'按预期工作。但是,任何显示的错误消息都将始终使用英语。好的。

如果您的shell是bashzsh,那么有一个既健壮又相当简单的解决方案,只使用符合posix的find功能;虽然bash本身不是posix的一部分,但大多数现代Unix平台都附带它,使得该解决方案可广泛移植:好的。

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

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

  • >(...)是一种(很少使用的)输出过程替换,它允许将输出(在这种情况下,stderr输出(2>重定向到>(...)内命令的stdin)。除了bashzsh之外,ksh原则上也支持它们,但试图将它们与stderr的重定向结合起来,正如这里所做的(2> >(...)一样),似乎被默默忽视(在ksh 93u+中)。好的。

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

这种方法是:好的。

  • 健壮:grep仅适用于错误消息(而不适用于文件路径和错误消息的组合,可能导致误报),并将拒绝权限的错误消息以外的错误消息传递给stderr。好的。

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

符合POSIX的解决方案:

完全符合POSIX的解决方案要么有局限性,要么需要额外的工作。好的。

如果find的输出无论如何都要捕获到一个文件中(或完全禁止),那么Jonathan Leffler的答案中基于管道的解决方案是简单、健壮和符合POSIX的:好的。

1
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。好的。

以下变更涉及:好的。

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

现在,退出代码指示是否发生除Permission denied以外的任何错误:1如果发生,则0否则。换句话说:退出代码现在反映了命令的真实意图:如果没有任何错误或只发生了拒绝许可错误,则报告成功(0)。可以说,这比通过find的退出代码(如顶部的解决方案)要好得多。好的。

注释中的gniourf_gniourf使用复杂的重定向(即使打印到stdout的文件路径的默认行为也有效)提出了此解决方案的(仍然符合posix)泛化:好的。

1
{ 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,但可以应用与上述相同的修复方法。好的。关于现有答案的说明:

  • 关于迈克尔·布鲁克斯的回答,有几点值得注意,find . ! -readable -prune -o -print:好的。

    • 它需要GNU find;特别是,它不能在MacOS上工作。当然,如果您只需要命令就可以与GNU find一起工作,那么这对您来说就不是问题了。好的。

    • 有些Permission denied错误可能仍然会出现:find ! -readable -prune报告当前用户确实拥有r权限,但缺少x可执行权限的目录的子项存在此类错误。原因是因为目录本身是可读的,所以没有执行-prune,并且试图下降到该目录中会触发错误消息。也就是说,典型的情况是r许可证丢失。好的。

    • 注意:以下是一个哲学和/或特定用例的问题,您可能会认为它与您无关,并且该命令非常适合您的需要,特别是如果您只需打印路径:好的。

      • 如果您将权限拒绝错误消息的过滤概念化为一个单独的任务,希望能够应用于任何find命令,那么主动预防权限拒绝错误的相反方法需要在find命令中引入"noise",这也会带来复杂性和逻辑缺陷。
      • 例如,对迈克尔的回答(截至本文撰写之时)的投票最多的评论试图说明如何通过包括-name过滤器来扩展命令,如下所示:find . ! -readable -prune -o -name '*.txt'但是,这并不能按预期工作,因为后面的-print动作是必需的(在这个答案中可以找到解释)。这样的微妙之处会引入错误。
  • Jonathan Leffler的回答中的第一个解决方案是find . 2>/dev/null > files_and_folders,正如他自己所说的,盲目地使所有错误消息静音(他也解释说,解决方法既繁琐又不完全可靠)。然而,从实际意义上讲,这是最简单的解决方案,因为您可以满足于假设任何和所有错误都与权限相关。好的。

  • Mist的答案sudo find . > files_and_folders简洁实用,但出于安全原因,除了打印文件名之外,其他任何东西都不明智:因为您以根用户身份运行,"您可能会因为查找或恶意版本中的错误,或意外写入某些内容的错误调用而使整个系统陷入混乱,这是C如果您以普通权限运行此程序,则不会发生这种情况"(来自Mist的三倍回复评论)。好的。

  • viraptor答案中的第二个解决方案是find . 2>&1 | grep -v 'Permission denied' > some_file,它存在误报的风险(由于通过管道发送stdout和stderr的混合信息),并且可能不会通过stderr报告非权限拒绝错误,而是沿着输出文件中的输出路径捕获这些错误。好的。

好啊。


如果要从根目录"/"开始搜索,您可能会看到如下输出:

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

这是因为许可。解决这个问题:

  • 您可以使用sudo命令:sudo find /. -name 'toBeSearched.file'。它询问超级用户的密码,当输入密码时,您将看到您真正想要的结果。

  • 您可以使用将标准错误输出从(通常是显示/屏幕)重定向到某些文件,并避免在屏幕上看到错误消息!重定向到特殊文件/dev/null:

    1
    find /. -name 'toBeSearched.file' 2>/dev/null
  • 您可以使用重定向将标准错误输出从(通常是显示/屏幕)重定向到标准输出(通常是显示/屏幕),然后使用带有-v"invert"参数的grep命令进行管道连接,以不查看具有"拒绝权限"字对的输出行:

    1
    find /. -name 'toBeSearched.file' 2>&1 | grep -v 'Permission denied'

  • 我不得不使用:

    1
    find / -name expect 2>/dev/null

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

    Expect是我正在搜索的Expect程序的位置。


    使用2>/dev/null将stderr连接到/dev/null

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


    您还可以使用-perm-prune谓词,以避免下降到不可读的目录中(另请参见如何从find程序中删除"permission denied"打印输出语句?-Unix和Linux堆栈交换):

    1
    find . -type d ! -perm -g+r,u+r,o+r -prune -o -print > files_and_folders


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

    1
    find . 2>/dev/null >files_and_folders

    虽然上述方法不能解决mac os x的问题,因为mac os x不支持-readable开关,这是避免输出中出现"拒绝权限"错误的方法。这可能对某人有帮助。

    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


    这些错误被输出到标准错误输出(fd2)。要筛选它们,只需将所有错误重定向到/dev/null:

    1
    find . 2>/dev/null > some_file

    或者先连接stderr和stdout,然后将这些特定错误变为grep:

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

    简单回答:

    find . > files_and_folders 2>&-

    2>&-关闭(-)标准错误文件描述符(2),因此所有错误消息都被静默。

    • 如果不打印任何"EDOCX1"〔5〕错误,则退出代码仍为1

    GNU find的可靠答案:

    find . -type d \! \( -readable -executable \) -prune -print -o -print > files_and_folders

    将额外的选项传递给find,即-prune(防止下降到)但仍然是-print,任何目录(-typed)没有(\!同时拥有-readable-executable权限,或(-o-print任何其他文件。

    • -readable-executable选项是GNU扩展,不是POSIX标准的一部分。
    • 可能仍然会在异常/损坏的文件上返回"Permission denied"(例如,请参阅影响使用lxcfs

    与任何与posix兼容的find一起工作的可靠答案(GNU、OSX/BSD等)

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

    使用管道将标准错误流传递到grep,删除包含'Permission denied'字符串的所有行。

    LC_ALL=C使用环境变量3>&2 2>&1 1>&33>&2 2>&1复制文件描述符设置posix区域设置,将标准错误流传输到grep[ $? = 1 ]使用[]反转grep返回的错误代码,以近似find的原始行为。

    • 还将过滤由于输出重定向而导致的任何'Permission denied'错误(例如,如果files_and_folders文件本身不可写)


    使用

    1
    sudo find / -name file.txt

    这是愚蠢的(因为你提高了搜索率)和不安全的,但写起来要短得多。


    为了避免出现权限被拒绝的警告,请告诉find通过从搜索中删除不可读文件来忽略它们。将表达式作为"或"添加到查找中,例如

    1
    find / \! -readable -prune -o -name '*.jbd' -ls

    这主要是说要(匹配一个不可读的文件并从列表中删除它)或(匹配一个像*.jbd这样的名称并用ls显示它)。(请记住,默认情况下,除非您使用-or,否则表达式是和是一起使用的。)您需要在第二个表达式中使用-ls,否则find可能会添加一个默认操作来显示匹配项,这也会显示所有不可读的文件。

    但是,如果您在系统上查找真正的文件,通常没有理由在/dev中查找,因为/dev有许多文件,所以您应该添加一个排除该目录的表达式,例如:

    1
    find / -mount \! -readable -prune  -o  -path /dev -prune  -o  -name '*.jbd' -ls

    所以(匹配不可读文件并从列表中删除)或(匹配路径/dev并从列表中删除)或(匹配文件如*.jbd并显示它)。


    以上所有的答案对我都不起作用。我在互联网上找到的任何东西都集中在:隐藏错误。无可正确处理进程返回代码/退出代码。我在bash脚本中使用命令find来定位一些目录,然后检查它们的内容。我使用退出代码来评估命令find success:值为零有效,否则失败。

    上面由迈克尔·布鲁克斯提供的答案有时是可行的。但我有一个场景失败了!我发现了这个问题并自己解决了。我需要在以下情况下删除文件:

    1
    it is a directory AND has no read access AND/OR has no execute access

    这里的关键问题是:和/或。我读到的一个好的建议条件序列是:

    1
    -type d ! -readable ! -executable -prune

    这并不总是有效的。这意味着当匹配为:

    1
    it is directory AND no read access AND no execute access

    当授予读取访问权限但没有执行访问权限时,此表达式序列失败。

    经过一些测试,我意识到了这一点,并将shell脚本解决方案更改为:

    nice find /home*/ -maxdepth 5 -follow \
        \( -type d -a ! \( -readable -a -executable \) \) -prune \
        -o \
        \( -type d -a -readable -a -executable -a -name"${m_find_name}" \) -print

    这里的关键是为组合表达式放置"not true":

    1
    has read access AND has execute access

    否则它没有完全访问权,这意味着:修剪它。事实证明,这对我来说是可行的,以前的一个方案建议的解决方案失败了。

    我在评论部分提供以下问题的技术细节。如果细节过多,我道歉。

    • ?为什么使用命令nice?我想到这里了。最初我认为在查看整个文件系统时降低进程优先级会比较好。我意识到这对我来说毫无意义,因为我的脚本仅限于几个目录。我把最大深度降到了3。
    • ?为什么要在/home*内搜索?这与此线程无关。我通过使用非特权用户(不是根用户)编译源代码手动安装所有应用程序。它们安装在"家庭"中。我可以有多个二进制文件和版本住在一起。我需要定位所有目录,以主从方式检查和备份。我可以有多个"/home"(多个磁盘在专用服务器中运行)。
    • ?为什么使用-跟踪?用户可以创建指向目录的符号链接。它的有用性取决于,我需要记录下找到的绝对路径。


    你可以使用grep-v反转匹配

    1
    -v, --invert-match        select non-matching lines

    这样地:

    1
    2
    find . > files_and_folders
    cat files_and_folders | grep -v"permission denied"> files_and_folders

    应该变魔术


    = MaOS=

    使用alias生成新命令:只需加载项~/。bash_profile line:

    1
    alias search='find / -name $file 2>/dev/null'

    在新的终端窗口中,您可以称之为:

    1
    $ file=<filename or mask>; search

    for example:

    $ file=etc; search


    如果您使用的是CSH或TCSH,下面是一个解决方案:

    1
    ( find . > files_and_folders ) >& /dev/null

    如果要输出到终端:

    1
    ( find . > /dev/tty ) >& /dev/null

    但是,正如"csh whynot"常见问题解答所描述的,您不应该使用csh。


    您还可以将查找结果放在文件中。

    找到。-名称'name ofsearchedfile'>>results.txt