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

find . > files_and_folders

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

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

如何同时隐藏错误?


当前回答

注意:

这个答案可能比用例更深入,在许多情况下,找到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.

其他回答

上面的答案对我都没用。我在网上找到的东西都集中在:隐藏错误。没有正确处理进程返回码/退出码。我在bash脚本中使用命令find来定位一些目录,然后检查它们的内容。我计算命令查找成功使用退出代码:值为零工作,否则失败。

Michael Brux提供的答案有时是有效的。但我有一个失败的场景!我发现了问题,自己解决了。我需要修剪文件时:

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

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

-type d ! -readable ! -executable -prune

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

it is directory AND no read access AND no execute access

当授予读访问权限而不授予执行访问权限时,此表达式序列将失败。

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

find /home*/ -maxdepth 5 -follow \ \(type d -a !\(-readable -a -executable \) \) -删除\ - o \ \(-type d -a -readable -a -executable -name "${m_find_name}" \) -打印

这里的关键是在组合表达式中放置“not true”:

has read access AND has execute access

否则它没有完全访问权限,这意味着:修剪它。事实证明,在一个之前建议的解决方案失败的场景中,这对我来说是有效的。

我在评论部分提供了以下技术细节。如果细节过多,我很抱歉。

¿Why using command nice? I got the idea here. Initially I thought it would be nice to reduce process priority when looking an entire filesystem. I realized it makes no sense to me, as my script is limited to few directories. I reduced -maxdepth to 3. ¿Why search within /home*/? This it not relevant for this thread. I install all applications by hand via source code compile with non privileged users (not root). They are installed within "/home". I can have multiple binaries and versions living together. I need to locate all directories, inspect and backup in a master-slave fashion. I can have more than one "/home" (several disks running within a dedicated server). ¿Why using -follow? Users might create symbolic links to directories. It's usefulness depends, I need to keep record of the absolute paths found.

您可以使用grep -v逆匹配

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

是这样的:

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

应该有魔力

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

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

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中的糟糕输出…运行时间似乎是相等的。

重定向标准错误。例如,如果你在unix机器上使用bash,你可以像这样将标准错误重定向到/dev/null:

find . 2>/dev/null >files_and_folders