我需要隐藏所有被拒绝的消息:

find . > files_and_folders

当这样的信息出现时,我正在做实验。我需要收集所有的文件夹和文件,它没有出现。

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

如何同时隐藏错误?


当前回答

为了在整个文件系统中搜索某些文件,例如主机,除了会导致各种错误的/proc树,我使用以下方法:

# find / -path /proc ! -prune -o -name hosts -type f
/etc/hosts

注意:因为-prune始终为真,所以必须对其求反,以避免在输出中看到行/proc。我试过了!-readable方法,并发现它返回/proc下当前用户可以读取的所有内容。所以"OR"条件并不是你所期望的。

我从find手册页给出的示例开始,参见-prune选项。

其他回答

简单的回答是:

找到。> files_and_folders 2>&-

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

如果将打印任何“权限拒绝”错误,退出代码仍将为1

健壮的GNU答案找到:

找到。type d \!\(-readable -executable \) -prune -print -o -print >文件和文件夹

传递额外的选项来查找-prune(防止降为),但仍然-print任何(-typed)不(\!)具有-可读和-可执行权限的目录,或(-o) -print任何其他文件。

-readable和-executable选项是GNU扩展,不是POSIX标准的一部分 可能仍然会对异常/损坏的文件返回'Permission denied'(例如,参见使用lxcfs < v2.0.5影响容器挂载文件系统的错误报告)

健壮的答案,适用于任何posix兼容的查找(GNU, OSX/BSD等)

{LC_ALL=C查找。3>&2 2>&1 1>&3 > files_and_folders | grep -v '权限被拒绝';[$ ?= 1];} 3>&2 2>&1

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

LC_ALL=C使用一个环境变量设置POSIX区域设置,3>&2 2>&1 1>&3和3>&2 2>&1重复文件描述符将标准错误流管道到grep,并且[$?= 1]使用[]来反转grep返回的错误代码,以近似find的原始行为。

也会过滤任何由于输出重定向导致的“权限拒绝”错误(例如,如果files_and_folders文件本身是不可写的)

为了在整个文件系统中搜索某些文件,例如主机,除了会导致各种错误的/proc树,我使用以下方法:

# find / -path /proc ! -prune -o -name hosts -type f
/etc/hosts

注意:因为-prune始终为真,所以必须对其求反,以避免在输出中看到行/proc。我试过了!-readable方法,并发现它返回/proc下当前用户可以读取的所有内容。所以"OR"条件并不是你所期望的。

我从find手册页给出的示例开始,参见-prune选项。

注意:

这个答案可能比用例更深入,在许多情况下,找到2>/dev/null就足够了。从跨平台的角度来看,它可能仍然是有兴趣的,它讨论了一些先进的shell技术,以便找到尽可能健壮的解决方案,尽管所防范的情况可能主要是假设的。


如果你的shell是bash或zsh,有一种解决方案既健壮又相当简单,只使用posix兼容的查找特性;虽然bash本身不是POSIX的一部分,但大多数现代Unix平台都附带了它,使得这个解决方案具有广泛的可移植性:

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

注意:

If your system is configured to show localized error messages, prefix the find calls below with LC_ALL=C (LC_ALL=C find ...) to ensure that English messages are reported, so that grep -v 'Permission denied' works as intended. Invariably, however, any error messages that do get displayed will then be in English as well. >(...) is a (rarely used) output process substitution that allows redirecting output (in this case, stderr output (2>) to the stdin of the command inside >(...). In addition to bash and zsh, ksh supports them as well in principle, but trying to combine them with redirection from stderr, as is done here (2> >(...)), appears to be silently ignored (in ksh 93u+). grep -v 'Permission denied' filters out (-v) all lines (from the find command's stderr stream) that contain the phrase Permission denied and outputs the remaining lines to stderr (>&2). Note: There's a small chance that some of grep's output may arrive after find completes, because the overall command doesn't wait for the command inside >(...) to finish. In bash, you can prevent this by appending | cat to the command.

这种方法是:

鲁棒性:grep仅应用于错误消息(而不应用于文件路径和错误消息的组合,可能会导致误报),并且将权限拒绝的错误消息以外的错误消息传递给stderr。 副作用free: find的退出代码被保留:无法访问遇到的至少一个文件系统项导致退出代码1(尽管它不会告诉您是否发生了权限拒绝之外的错误)。


posix兼容的解决方案:

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

如果find的输出无论如何都要被捕获到文件中(或者完全被抑制),那么Jonathan Leffler给出的基于管道的解决方案是简单、健壮且符合posix的:

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

注意,重定向的顺序很重要:2>和1必须先出现。

预先在文件中捕获stdout输出允许2>&1仅通过管道发送错误消息,然后grep可以明确地对其进行操作。

The only downside is that the overall exit code will be the grep command's, not find's, which in this case means: if there are no errors at all or only permission-denied errors, the exit code will be 1 (signaling failure), otherwise (errors other than permission-denied ones) 0 - which is the opposite of the intent. That said, find's exit code is rarely used anyway, as it often conveys little information beyond fundamental failure such as passing a non-existent path. However, the specific case of even only some of the input paths being inaccessible due to lack of permissions is reflected in find's exit code (in both GNU and BSD find): if a permissions-denied error occurs for any of the files processed, the exit code is set to 1.

下面的变体解决了这个问题:

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

现在,退出代码指示是否发生了除Permission denied之外的任何错误:如果是1,否则为0。 换句话说:退出码现在反映了命令的真实意图:如果根本没有错误或只发生了权限拒绝错误,则报告success(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的,但可以应用与上面相同的修复。


关于现有答案的说明:

There are several points to note about Michael Brux's answer, find . ! -readable -prune -o -print: It requires GNU find; notably, it won't work on macOS. Of course, if you only ever need the command to work with GNU find, this won't be a problem for you. Some Permission denied errors may still surface: find ! -readable -prune reports such errors for the child items of directories for which the current user does have r permission, but lacks x (executable) permission. The reason is that because the directory itself is readable, -prune is not executed, and the attempt to descend into that directory then triggers the error messages. That said, the typical case is for the r permission to be missing. Note: The following point is a matter of philosophy and/or specific use case, and you may decide it is not relevant to you and that the command fits your needs well, especially if simply printing the paths is all you do: If you conceptualize the filtering of the permission-denied error messages a separate task that you want to be able to apply to any find command, then the opposite approach of proactively preventing permission-denied errors requires introducing "noise" into the find command, which also introduces complexity and logical pitfalls. For instance, the most up-voted comment on Michael's answer (as of this writing) attempts to show how to extend the command by including a -name filter, as follows: find . ! -readable -prune -o -name '*.txt' This, however, does not work as intended, because the trailing -print action is required (an explanation can be found in this answer). Such subtleties can introduce bugs. The first solution in Jonathan Leffler's answer, find . 2>/dev/null > files_and_folders, as he himself states, blindly silences all error messages (and the workaround is cumbersome and not fully robust, as he also explains). Pragmatically speaking, however, it is the simplest solution, as you may be content to assume that any and all errors would be permission-related. mist's answer, sudo find . > files_and_folders, is concise and pragmatic, but ill-advised for anything other than merely printing filenames, for security reasons: because you're running as the root user, "you risk having your whole system being messed up by a bug in find or a malicious version, or an incorrect invocation which writes something unexpectedly, which could not happen if you ran this with normal privileges" (from a comment on mist's answer by tripleee). The 2nd solution in viraptor's answer, find . 2>&1 | grep -v 'Permission denied' > some_file runs the risk of false positives (due to sending a mix of stdout and stderr through the pipeline), and, potentially, instead of reporting non-permission-denied errors via stderr, captures them alongside the output paths in the output file.

GNU find†的优化解决方案

至少对于某些系统+文件系统组合,find不需要统计文件来获取其类型。然后,您可以在测试可读性之前检查它是否是一个目录,以加快搜索‡-我在测试中获得了约30%的改进。所以对于长时间的搜索或经常运行的搜索,可以使用其中之一:

打印所有可见的东西

$ find . -print -type d ! -readable -prune
$ find . -type d ! -readable -prune , [expression] -print

打印可见文件

$ find . -type d \( ! -readable -prune -o -true \) -o [expression] -print

打印可见目录

$ find . -type d -print ! -readable -prune
$ find . -type d \( ! -readable -prune , [expression] -print \)

只打印可读目录

$ find . -type d ! -readable -prune -o [expression] -print

笔记

†可读的和,(逗号)操作符是GNU扩展。这个表达式

$ find . [expression] , [expression]

逻辑上等价于

$ find . \( [expression] -o -true \) [expression]

‡这是因为启用此优化的查找实现将避免在所讨论的用例中声明非目录文件。


编辑:shell函数

下面是一个POSIX shell函数,我最终将这个测试前置到任何表达式。它似乎可以很好地使用隐式的-print和命令行选项:

findr () {
    j=$#; done=
    while [ $j -gt 0 ]; do
        j=$(($j - 1))
        arg="$1"; shift
        test "$done" || case "$arg" in
            -[A-Z]*) ;;  # skip options
            -*|\(|!)     # find start of expression
                set -- "$@" \( -type d ! -readable -prune -o -true \)
                done=true
                ;;
        esac
        set -- "$@" "$arg"
    done
    find "$@"
}

答案中列出的其他两个替代方案导致POSIX shell中的语法错误(甚至不能源包含函数定义的文件)或ZSH中的糟糕输出…运行时间似乎是相等的。

您还可以使用-perm和-prune谓词来避免下降到不可读的目录(参见如何从查找程序中删除“拒绝权限”打印输出语句?- Unix和Linux Stack Exchange):

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