提问人:Léo Léopold Hertz 준영 提问时间:4/18/2009 最后编辑:JahidLéo Léopold Hertz 준영 更新时间:3/16/2022 访问量:637648
如何从“查找”中排除所有“权限被拒绝”消息?
How can I exclude all "permission denied" messages from "find"?
问:
我需要隐藏所有被拒绝的邮件:
find . > files_and_folders
当这样的消息出现时,我正在试验。我需要收集所有文件夹和文件,而不会出现。
是否可以将权限级别定向到文件?files_and_folders
如何同时隐藏错误?
答:
用:
find . 2>/dev/null > files_and_folders
当然,这不仅隐藏了错误,还隐藏了所有错误消息。Permission denied
如果你真的想保留其他可能的错误,比如符号链接上的跳跃太多,而不是权限被拒绝的错误,那么你可能不得不猜测你没有很多文件叫做“权限被拒绝”,然后尝试:
find . 2>&1 | grep -v 'Permission denied' > files_and_folders
如果只想过滤标准误差,可以使用更复杂的构造:
find . 2>&1 > files_and_folders | grep -v 'Permission denied' >&2
命令上的 I/O 重定向为:。
管道将标准输出重定向到命令,并首先应用。将标准误差发送到与标准输出(管道)相同的位置。将标准输出(但不是标准错误)发送到文件。最终结果是,写入标准错误的消息被发送到管道中,而 的常规输出被写入文件。筛选标准输出(您可以决定它的选择性,并且可能必须根据区域设置和 O/S 更改拼写),最终意味着幸存的错误消息(写入标准输出)再次进入标准错误。最终重定向在终端上可以被视为可选的,但在脚本中使用它是一个非常好的主意,以便在标准错误上显示错误消息。find
2>&1 > files_and_folders |
grep
2>&1
> files_and_folders
find
grep
>&2
这个主题有无穷无尽的变化,这取决于你想做什么。这将适用于任何具有任何 Bourne shell 衍生物(Bash、Korn 等)和任何符合 POSIX 的 find 版本的 Unix
变体。
如果您希望适应系统上的特定版本,可能还有其他选项可用。特别是 GNU 有无数的选项,这是其他版本所没有的——参见当前公认的答案,了解其中一组选项。find
find
评论
2>/dev/null
2>
2>&1
2>&-
bash
/dev/null
permission denied
重定向标准错误。例如,如果你在 unix 机器上使用 bash,你可以将标准错误重定向到 /dev/null,如下所示:
find . 2>/dev/null >files_and_folders
使用 2>/dev/null 通过管道传递stderr
/dev/null
find . -name '...' 2>/dev/null
评论
find . -name '...' -print 2>/dev/null
这些错误将打印到标准错误输出 (fd 2) 中。要过滤掉它们,只需将所有错误重定向到 /dev/null:
find . 2>/dev/null > some_file
或者首先加入 stderr 和 stdout,然后 grep 出这些特定的错误:
find . 2>&1 | grep -v 'Permission denied' > some_file
我不得不使用:
find / -name expect 2>/dev/null
指定我想查找的名称,然后告诉它将所有错误重定向到 /dev/null
expect 是我正在搜索的 expect 程序的位置。
评论
expect
expect
如果你想从根“/”开始搜索,你可能会看到这样的输出:
find: /./proc/1731/fdinfo: Permission denied
find: /./proc/2032/task/2032/fd: Permission denied
这是因为许可。要解决此问题,请执行以下操作:
您可以使用 sudo 命令:
sudo find /. -name 'toBeSearched.file'
它询问超级用户的密码,输入密码时,您将看到您真正想要的结果。如果您没有使用 sudo 命令的权限,这意味着您没有超级用户的密码,请首先要求系统管理员将您添加到 sudoers 文件中。
您可以使用将标准错误输出从(通常显示/屏幕)重定向到某个文件,并避免在屏幕上看到错误消息!重定向到特殊文件 /dev/null :
find /. -name 'toBeSearched.file' 2>/dev/null
您可以使用将标准错误输出从(通常显示/屏幕)重定向到标准输出(通常显示/屏幕),然后使用带有 -v “invert” 参数的 grep 命令管道,以不查看具有“权限被拒绝”字对的输出行:
find /. -name 'toBeSearched.file' 2>&1 | grep -v 'Permission denied'
评论
sudo find...
您还可以使用 and 谓词来避免下降到不可读的目录(另请参阅如何从查找程序中删除“权限被拒绝”打印语句? - Unix & Linux Stack Exchange):-perm
-prune
find . -type d ! -perm -g+r,u+r,o+r -prune -o -print > files_and_folders
评论
-perm -g+r,u+r,o+r
仅匹配为文件的所有 3 个安全主体设置了 (read) 权限的文件,这与当前用户是否可以读取该文件没有直接关系。它既有可能错过当前用户可以读取的文件,也有可能匹配他们无法读取的文件。r
find . -type d ! \( -perm -u+r -o -perm -g+r -o -perm -o+r \) -prune -o -print
-readable
-perm
echo 'hi' > file; sudo chown nobody:nobody file; sudo chmod o-r file; find file -perm -u=r
file
nobody
cat file
用:
find . ! -readable -prune -o -print
或更一般地说
find <paths> ! -readable -prune -o <other conditions like -name> -print
- 以避免“权限被拒绝”
- 并且不禁止显示(其他)错误消息
- AND get exit status 0 (“所有文件都已成功处理”)
适用于: find (GNU findutils) 4.4.2. 背景:
- 测试匹配可读文件。当 test 为 false 时,运算符返回 true。并匹配不可读的目录 (&files)。
-readable
!
! -readable
- 该操作不会下降到目录中。
-prune
! -readable -prune
可以翻译为:如果目录不可读,请不要下降到它。- 该测试考虑了测试忽略的访问控制列表和其他权限工件。
-readable
-perm
参见 find
(1) 手册页以获取更多详细信息。
评论
-o
find . ! -readable -prune -o -name '*.txt'
find
-readable
find
find
find
find . ! -readable -prune -o -name '*.txt'
似乎无法使用 find 14.04 在 Ubuntu 4.2.2 上运行。它似乎使.出于某种奇怪的原因,我成功了-name
find . \( ! -readable -prune \) -o -name '*.txt' -print
为了避免仅出现权限被拒绝的警告,请通过从搜索中修剪无法读取的文件来告诉 find 忽略它们。将表达式作为 OR 添加到查找中,例如
find / \! -readable -prune -o -name '*.jbd' -ls
这主要说(匹配一个不可读的文件并将其从列表中删除)或(匹配一个像 *.jbd 这样的名称并显示它 [with ls])。(请记住,默认情况下,表达式是 AND 在一起的,除非您使用 -or。您需要在第二个表达式中使用 -ls,否则 find 可能会添加一个默认操作来显示任一匹配项,这也将显示所有不可读的文件。
但是,如果你在系统上查找真实文件,通常没有理由在 /dev 中查找,因为 /dev 有很多文件,所以你应该添加一个排除该目录的表达式,例如:
find / -mount \! -readable -prune -o -path /dev -prune -o -name '*.jbd' -ls
所以(匹配不可读的文件并从列表中修剪)或(匹配路径 /dev 并从列表中修剪)或(匹配像 *.jbd 这样的文件并显示它)。
用
sudo find / -name file.txt
这很愚蠢(因为你提升了搜索)和不安全,但写起来要短得多。
评论
sudo
find
虽然上述方法不能解决 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
评论
2>/dev/null
-exec du -ch {} + 2>/dev/null | grep total$
-exec
find
du -ch file_pattern
file_pattern
file_pattern
du
grep total
注意:
- 这个答案可能比用例所保证的更深入,
并且在许多情况下发现 2>/dev/null
可能足够好。对于跨平台视角和对一些高级 shell 技术的讨论,它可能仍然很有意义,以找到尽可能强大的解决方案,即使所防范的情况可能在很大程度上是假设的。
如果你的 shell 是 bash
或 zsh
,那么有一个解决方案既健壮又相当简单,只使用符合 POSIX 的查找
功能;虽然它本身不是 POSIX 的一部分,但大多数现代 Unix 平台都附带了它,这使得这个解决方案具有广泛的可移植性:bash
find . > files_and_folders 2> >(grep -v 'Permission denied' >&2)
注意:
如果您的系统配置为显示本地化错误消息,请在下面的
查找
调用前面加上LC_ALL=C
() 以确保报告英文消息,以便按预期工作。但是,显示的任何错误消息也总是以英语显示。LC_ALL=C find ...
grep -v 'Permission denied'
>(...)
是一个(很少使用的)输出进程替换,它允许将输出(在本例中为 stderr 输出 () 重定向到内部命令的 stdin。
除了 和 之外,原则上也支持它们,但尝试将它们与来自 stderr 的重定向相结合,就像这里所做的那样 (),似乎被默默地忽略了 (in )。2>
>(...)
bash
zsh
ksh
2> >(...)
ksh 93u+
grep -v 'Permission denied'
过滤掉 () 包含短语的所有行(来自命令的 stderr 流),并将剩余的行输出到 stderr ()。-v
find
Permission denied
>&2
注意:某些输出可能会在完成后到达,因为整个命令不会等待内部的命令完成。在 中,您可以通过追加到命令来防止这种情况。
grep
find
>(...)
bash
| cat
这种方法是:
可靠:仅适用于错误消息(而不是文件路径和错误消息的组合,可能导致误报),并且将权限被拒绝的错误消息以外的错误消息传递到 stderr。
grep
副作用:保留的退出代码:无法访问遇到的至少一个文件系统项会导致退出代码(尽管这不会告诉您是否发生了除权限被拒绝的错误之外的错误(也))。
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
唯一的缺点是整个退出代码将是 grep
命令的,而不是 的,在这种情况下,这意味着:如果根本没有错误或只有权限拒绝错误,则退出代码将是(信号失败),否则(权限拒绝以外的错误) - 这与意图相反。
也就是说,find
的退出代码很少被使用,因为它通常除了基本故障(例如传递不存在的路径)之外几乎没有传达任何信息。
但是,由于缺乏权限而无法访问某些输入路径的特定情况反映在 的退出代码中(在 GNU 和 BSD 中):如果处理的任何文件发生权限拒绝错误,则退出代码设置为 。find
1
0
find
find
1
以下变体解决了以下问题:
find . 2>&1 >files_and_folders | { grep -v 'Permission denied' >&2; [ $? -eq 1 ]; }
现在,退出代码指示是否发生了除此以外的任何错误:如果是,则否则。
换言之:退出代码现在反映了命令的真实意图:如果根本没有错误或仅发生权限拒绝错误,则报告成功 ()。
可以说,这比仅仅传递 的退出代码要好,就像在顶部的解决方案中一样。Permission denied
1
0
0
find
注释中的gniourf_gniourf建议使用复杂的重定向对此解决方案进行(仍然符合 POSIX 标准)的泛化,即使将文件路径打印到 stdout 的默认行为也适用于:
{ find . 3>&2 2>&1 1>&3 | grep -v 'Permission denied' >&3; } 3>&2 2>&1
简而言之:自定义文件描述符用于临时交换 stdout () 和 stderr (),以便可以通过 stdout 单独传递错误消息。3
1
2
grep
如果没有这些重定向,数据(文件路径)和错误消息都将通过 stdout 通过管道传输,然后无法区分错误消息和名称恰好包含短语 的(假设)文件。grep
grep
Permission denied
Permission denied
但是,与第一个解决方案一样,报告的退出代码将是 's,而不是 's,但可以应用与上述相同的修复程序。grep
find
关于现有答案的说明:
关于迈克尔·布鲁克斯的回答,有几点需要注意:
find . ! -readable -prune -o -print
它需要 GNU ;值得注意的是,它不适用于 macOS。当然,如果您只需要该命令即可使用 GNU ,这对您来说不是问题。
find
find
某些错误可能仍会浮出水面:报告当前用户具有权限但缺少(可执行)权限的目录的子项的此类错误。原因是由于目录本身是可读的,因此不会执行,并且尝试下降到该目录会触发错误消息。也就是说,典型的情况是缺少许可。
Permission denied
find ! -readable -prune
r
x
-prune
r
注意:以下一点是哲学和/或特定用例的问题,您可能会认为它与您无关,并且该命令非常适合您的需求,特别是如果您只需打印路径即可:
- 如果将拒绝权限的错误消息的筛选概念化为一个单独的任务,并且希望能够应用于任何命令,则主动防止拒绝权限错误的相反方法需要在命令中引入“噪音”,这也会带来复杂性和逻辑陷阱。
find
find
- 例如,对 Michael 的回答(截至撰写本文时)获得最多票的评论试图展示如何通过包含过滤器来扩展命令,如下所示:
然而,这并不能按预期工作,因为尾随操作是必需的(可以在此答案中找到解释)。这种微妙之处可能会引入错误。-name
find . ! -readable -prune -o -name '*.txt'
-print
- 如果将拒绝权限的错误消息的筛选概念化为一个单独的任务,并且希望能够应用于任何命令,则主动防止拒绝权限错误的相反方法需要在命令中引入“噪音”,这也会带来复杂性和逻辑陷阱。
正如他自己所说,Jonathan Leffler 回答中的第一个解决方案是盲目地静音所有错误消息(正如他所解释的那样,解决方法很麻烦且不完全健壮)。然而,从实用的角度来看,这是最简单的解决方案,因为您可能满足于假设任何和所有错误都与权限相关。
find . 2>/dev/null > files_and_folders
Mist 的回答简洁而务实,但出于安全原因,除了打印文件名之外,其他任何事情都是不明智的:因为你是以 root 用户身份运行的,“你可能会因为 find 中的错误或恶意版本而弄乱整个系统,或者一个不正确的调用,它意外地写入了一些东西,如果你以正常权限运行它,这不会发生”(来自 Tripleee 对 Mist 回答的评论)。
sudo find . > files_and_folders
viraptor 回答中的第二个解决方案存在误报的风险(由于通过管道发送 stdout 和 stderr 的混合),并且可能不是通过 stderr 报告非权限拒绝的错误,而是将它们与输出文件中的输出路径一起捕获。
find . 2>&1 | grep -v 'Permission denied' > some_file
评论
find . 2>&1 > files_and_folders | grep -v 'Permission denied' >&2
{ find . 3>&2 2>&1 1>&3 | grep -v 'Permission denied' >&3; } 3>&2 2>&1
>(...)
find
find
execute/search
find
read
以上答案都不适合我。无论我在互联网上找到什么,都集中在:隐藏错误。没有正确处理进程返回代码/退出代码。我在 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 脚本解决方案更改为:
很好找到 /home*/ -maxdepth 5 -follow \ \( -type d -a ! \( -可读 -a -可执行 \) \) -prune \ -o \ \( -type d -a -可读 -a -可执行 -a
-name “${m_find_name}”
这里的关键是将“not true”放在组合表达式中:
has read access AND has execute access
否则,它没有完全访问权限,这意味着:修剪它。事实证明,这在以前建议的解决方案失败的一种情况下对我有用。
对于评论部分的问题,我提供了以下技术细节。如果细节过多,我深表歉意。
- ¿为什么要使用命令nice?我在这里得到了这个想法。最初,我认为在查看整个文件系统时降低进程优先级会很好。我意识到这对我来说毫无意义,因为我的脚本仅限于几个目录。我将 -maxdepth 减少到 3。
- ¿为什么要在 /home*/ 中搜索?这与此线程无关。我通过源代码手动安装所有应用程序,使用非特权用户(不是 root)编译。它们安装在“/home”中。我可以让多个二进制文件和版本共存。我需要找到所有目录,以主从方式进行检查和备份。我可以有多个“/home”(在专用服务器中运行的多个磁盘)。
- ¿为什么要使用 -follow?用户可能会创建指向目录的符号链接。它的用处取决于,我需要记录找到的绝对路径。
评论
nice
find $HOME -maxdepth 5 -follow ...
${m_find_name}
nice
/home*
-maxdepth 5
-follow
简单的答案:
find . > files_and_folders 2>&-
2>&
- 关闭 (-
) 标准错误文件描述符 (2
),以便所有错误消息静音。
- 如果打印任何“”错误,退出代码仍将是
1
Permission denied
GNU的鲁棒性答案:find
find . -type d \! \( -readable -executable \) -prune -print -o -print > files_and_folders
传递额外的选项以查找
-prune
(防止下降到)但仍然 -
print 任何没有 (
\!
) 和 -executable
权限的目录 (-type
d
),或 (-o
) -print
任何其他文件。
-readable
和-executable
选项是 GNU 扩展,不是 POSIX 标准的一部分- 在异常/损坏的文件上仍可能返回“”(例如,请参阅使用 < v2.0.5 影响容器挂载文件系统的错误报告)
Permission denied
lxcfs
适用于任何 POSIX 兼容(GNU、OSX/BSD 等)的可靠答案find
{ LC_ALL=C find . 3>&2 2>&1 1>&3 > files_and_folders | grep -v 'Permission denied'; [ $? = 1 ]; } 3>&2 2>&1
使用管道将标准错误流传递给 grep
,删除包含“权限被拒绝”
字符串的所有行。
LC_ALL=C
使用环境变量 3>&2 2>&1 1>&3 和 3>&2 2>&
1 重复文件描述符设置 POSIX 语言环境,将标准错误流管道传递给 grep,[ $? = 1
] 使用 []
反转
grep
返回的错误代码,以近似 find
的原始行为。
- 还将过滤由于输出重定向而导致的任何错误(例如,如果文件本身不可写)
'Permission denied'
files_and_folders
评论
-perm
echo 'hi' > file; sudo chown nobody:nobody file; sudo chmod o-r file; find file -perm -u=r
file
nobody
cat file
-perm
你可以使用 grep -v invert-match
-v, --invert-match select non-matching lines
喜欢这个:
find . > files_and_folders
cat files_and_folders | grep -v "permission denied" > files_and_folders
应该到魔法
-=对于 MacOS=-
使用别名创建一个新命令:只需添加 ~/.bash_profile 行:
alias search='find / -name $file 2>/dev/null'
在新的终端窗口中,您可以调用它:
$ file=<filename or mask>; search
例如:
$ 文件=etc;搜索
如果您使用的是 CSH 或 TCSH,则有一个解决方案:
( find . > files_and_folders ) >& /dev/null
如果要输出到终端:
( find . > /dev/tty ) >& /dev/null
但是,正如“csh-whynot”常见问题解答所描述的那样,您不应该使用 CSH。
评论
只需使用它来搜索系统中的文件即可。
find / -name YOUR_SEARCH_TERM 2>&1 | grep YOUR_SEARCH_TERM
我们不要做不必要的过度工程,你只是想搜索你的文件,对吧?然后,该命令将为您列出文件,如果它们存在于您可以访问的区域中。
最小的解决方案只是添加标志。readable
find . -name foo -readable
针对 GNU † 的优化解决方案find
至少对于某些系统+文件系统组合,不需要文件来获取其类型。然后,您可以在测试可读性之前检查它是否是一个目录,以加快搜索速度‡ - 我在所做的测试中改进了大约 30%。因此,对于长时间搜索或运行频率足够高的搜索,请使用以下方法之一:find
stat
打印所有可见内容
$ 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
笔记
†and(逗号)运算符是 GNU 扩展。此表达式-readable
,
$ find . [expression] , [expression]
在逻辑上等价于
$ find . \( [expression] -o -true \) [expression]
‡这是因为在所讨论的用例中,启用此优化的实现将完全避免使用非目录文件。find
stat
编辑: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 中的输出错误......运行时间似乎是等效的。
为了在整个文件系统中搜索某些文件,例如,除了导致各种错误的树之外,我使用以下方法:hosts
/proc
# find / -path /proc ! -prune -o -name hosts -type f
/etc/hosts
注意:由于 is 始终为 true,因此必须否定它以避免在输出中看到该行。我尝试了使用方法,发现它在 /proc 下返回当前用户可以读取的各种内容。因此,“OR”条件不会达到您的期望/想要。-prune
/proc
! -readable
我从 find 手册页给出的示例开始,请参阅选项。-prune
评论
find /. -name 'toBeSearched.file' 2>/dev/null
/proc
-path
-prune