Answers:
您尝试的操作无效,因为access denied
输出是错误,并通过STDERR而不是通过管道发送到的STDOUT发送grep
。
您可以通过仅重定向STDERR来避免看到这些错误
find /home -iname "*.pdf" 2>/dev/null
或正如David Foerster所说,我们可以更简洁地关闭 STDERR
find /home -iname "*.pdf" 2>&-
但是,我怀疑您实际上只想搜索房屋而不是其他用户的房屋,所以也许您真的想要
find ~ -iname "*.pdf"
如果这引发了错误,则您的本地配置中可能存在一些错误的所有权,应对此进行调查。
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
。如果GNU find尝试将错误消息写入功能失常的文件描述符,则不会终止自身。对于所有权问题sudo chown -R $USER: ...
,如果其中有更多文件不归所有权,则问题会更有效$USER
。
被拒绝的访问可能正在打印stderr
而不是stdout
。
尝试这个:
find /home -iname "*.pdf" 2>&1 | grep -v "access denied"
该2>&1
重定向从输出stderr
到stdout
,这样grep -v
可以做自己的工作。(默认情况下,|
仅管道stdout
而不是管道stderr
。)
2>&1
...我不是bash专家,所以如果那是不正确的,请这样做:)
bash
在Ubuntu中有它,除了POSIX模式。我认为这是最好的解决方案-被恶意命名的文件access denied
仍会出现。
您可能是说“权限被拒绝”,find
而不是“访问被拒绝”,Ubuntu在您因文件权限而无法访问某些内容时向您显示。
可以正确执行此操作的一个完全通用的命令(此外,只要错误消息相同,就可以移植到其他* nix es上):
(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
(通常,您要将一些参数传递给find
。这些参数在第一次重定向之前3>&1
。)
但是,通常您可以使用更简单的方法。例如,您可能可以使用流程替换。详细信息如下。
两种典型的方法是丢弃stderr(如Zanna的答案)或将stderr重定向到stdout并过滤stdout(如Android Dev的answer)。尽管它们具有编写简单且通常是合理选择的优势,但这些方法并不理想。
摒弃一切都发送到stderr通过它重定向到-如空设备用2>/dev/null
或用其关闭2>&-
-runs失踪不是“权限被拒绝”其他错误的风险。
“权限被拒绝”可能是运行时看到的最常见错误find
,但它远非唯一可能的错误,如果确实发生了另一个错误,您可能想了解一下。特别是,find
如果不存在起点,则报告“没有这样的文件或目录”。从多个起点出发,find
可能仍会返回一些有用的结果,并且似乎可以正常工作。例如,如果a
和c
存在但b
不存在,则find a b c -name x
在中打印结果a
,然后在中显示“没有此类文件或目录” b
,然后在中显示结果c
。
与stdout和stderr一起组合成stdout并将其通过管道传递到grep
或其他命令进行过滤(与2>&1 | grep ...
或|& grep ...
一样),可能会无意间过滤掉名称包含要过滤的消息的文件。
例如,如果您过滤掉包含“拒绝权限”的行,那么您还将删除显示诸如“拒绝权限messages.txt”之类文件名的搜索结果。这可能是偶然发生的,尽管也可以为文件指定特制名称以阻止搜索。
过滤合并的流还有另一个问题,无法通过更选择性地进行过滤(例如grep -vx 'find: .*: Permission denied'
在管道的右侧)来缓解。某些find
操作(包括-print
您未指定任何操作时隐式的操作)会根据stdout是否为终端来确定如何输出文件名。
?
改为打印。grep
)传递搜索结果会导致find
不再看到终端。(更确切地说,它导致其stdout不成为终端。)然后,从字面上输出奇怪的字符。但是,如果管道右侧的所有命令都是(a)删除看起来像“权限被拒绝”消息的行,并且(b)打印剩下的内容,那么您仍然会受到find
终端的那种恶作剧的影响检测旨在防止。man find
更多信息,请参见的“非常规文件名”部分,包括打印文件名的每个操作的行为。(“许多人发现结果中数据的印刷是其他用户的控制下的行动......的”)也见部分3.3.2.1,3.3.2.2和3.3.2.3中的GNU查找工具组的参考手册。上面关于异常文件名的讨论与GNU find有关,后者是在find
包括Ubuntu在内的GNU / Linux系统中的实现。
您真正想要的是在将stderr连接到时保持stdout不变。不幸的是,这没有简单的语法。通过管道将stdout 进行管道传输,并提供一些外壳程序(包括)来管道传输这两个流,或者您可以先使用将stderr重定向到stdout ,其效果相同。但是常用的shell并没有提供仅管道stderr的语法。grep
|
bash
|&
2>&1 |
您仍然可以这样做。真尴尬 一种方法是将stdout与stderr交换,以使搜索结果在stderr上,错误在stdout上,然后将stdout传递到grep
进行过滤:
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
通常,您会将参数传递给find
,例如起点(搜索的位置,通常是目录)和谓词(测试和操作)。这些代替args
上面。
通过引入一个新的文件描述符来保持要交换的两个标准流之一,然后执行重定向以交换它们,并关闭新的文件描述符,可以起作用。
3>&1
将文件描述符3重定向到stdout,以便随后重定向stdout(文件描述符1)时,仍可以轻松地写入原始stdout。1>&2
将stdout重定向到stderr。由于文件描述符3仍然是原始标准输出,因此仍可以对其进行访问。2>&3
将stderr重定向到文件描述符3,即原始stdout。3>&-
关闭不再需要的文件描述符3。但是,此方法的缺点是将搜索结果发送到stderr并将错误发送到stdout。如果您是直接在交互式外壳中运行此命令,而不是进一步管道传输或重定向输出,那么这实际上并不重要。否则,可能是一个问题。如果将该命令放在脚本中,然后有人(也许您以后会)将其输出重定向或传递给管道,则它的行为将不会达到预期的效果。
解决方案是在完成输出过滤之后将流交换回去。在管道的右侧应用上面显示的相同重定向将无法实现此目的,因为|
仅管道stdout,因此管道的那一侧仅接收最初发送到stderr的输出(因为交换了流),而不是原始的标准输出。相反,您可以用于在子外壳程序(related)中(
)
运行上述命令,然后将交换重定向应用于该命令:
(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
使分组工作的是分组而不是子shell。如果愿意,可以使用{
;}
:
{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-
某些shell(包括可以支持它的系统上的Bash)(包括GNU / Linux系统(如Ubuntu))使您可以执行进程替换,这使您可以运行命令并重定向至其中的一个流或从中重定向。您可以将find
命令的stderr 重定向到对其进行grep
过滤的命令,然后将该grep
命令的stdout 重定向到stderr。
find args 2> >(grep -Fv 'Permission denied' >&2)
幸得Android开发人员为这个想法。
尽管bash
支持进程替换,但sh
在Ubuntu中是dash
,但不支持。如果您尝试使用此方法,它将显示“语法错误:重定向意外”,而交换stdout和stderr的方法仍然可以使用。此外,当bash
以POSIX模式运行时,将关闭对进程替换的支持。
bash
在POSIX模式下运行的一种情况是将其调用为sh
1。因此,在像Fedora这样的OS上(其中bash
提供了)/bin/sh
,或者如果您已在Ubuntu上将/bin/sh
符号链接指向bash
自己,则在sh
没有事先关闭POSIX模式的命令的情况下,脚本中的进程替换仍然无法进行。如果您想在脚本中使用此方法,最好的选择是放在#!/bin/bash
顶部而不是#!/bin/sh
,如果还没有的话。
1:在这种情况下,在其启动脚本中运行命令后,bash
将自动打开POSIX模式。
能够测试这些命令很有用。为此,我创建了tmp
当前目录的子目录,并在其中填充了一些文件和目录,从其中一个文件中删除了权限,以触发中的“权限被拒绝”错误find
。
mkdir tmp; cd tmp; mkdir a b c; touch w a/x 'a/Permission denied messages.txt' b/y c/z; chmod 0 b
其中,该目录是可以访问包括其名称中有“权限被拒绝”的文件。find
在没有重定向或管道的情况下运行将显示此文件,但还会显示另一个无法访问的目录的实际“权限被拒绝”错误:
ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied
将stdout和stderr都管道到grep
并过滤出包含“权限被拒绝”的行将使错误消息消失,但也将名称中带有该短语的文件的搜索结果隐藏起来:
ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b
find 2>&1 | grep -Fv 'Permission denied'
是等效的,并产生相同的输出。
上面显示的仅从错误消息(而不是搜索结果)中过滤掉“拒绝权限”的方法是成功的。例如,这是交换stdout和stderr的方法:
ek@Io:~/tmp$ (find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find args 2> >(grep -Fv 'Permission denied' >&2)
产生相同的输出。
您可以触发其他错误消息,以确保仍然允许发送到stderr的行不包含文本“权限被拒绝”。例如,这里我find
以当前目录(.
)作为起点,但不存在的目录foo
作为另一个起点:
ek@Io:~/tmp$ (find . foo 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘foo’: No such file or directory
find
的标准输出仍然是终端我们还可以看到哪些命令使特殊字符(例如换行符)按原样显示。(这可以与上面的演示分开进行,并且不需要在tmp
目录中。)
制作名称中带有换行符的文件:
touch $'abc\ndef'
通常,我们使用目录作为的起点find
,但是文件也可以工作:
$ find abc*
abc?def
将stdout插入另一个命令会使换行符按字面输出,从而造成两个单独的搜索结果abc
和的错误印象def
。我们可以用cat
:
$ find abc* | cat
abc
def
仅重定向stderr不会导致此问题:
$ find abc* 2>/dev/null
abc?def
也不会关闭它:
$ find abc* 2>&-
abc?def
配管grep
确实会引起问题:
$ find abc* |& grep -Fv 'Permission denied'
abc
def
(更换|&
用2>&1 |
等效并产生相同的输出。)
交换stdout和stderr和管道的stdout确实不原因problem- find
的标准输出变为标准错误,这是不管道:
$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def
将该命令分组并交换回来的流不会导致此问题:
$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def
(该{
;}
版本产生相同的输出。)
使用进程替换来过滤stderr也不引起问题:
$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def