取出“拒绝访问”行


9

当我find用来查看目录中的所有pdf文件时/home,我看到的是access denied。为了消除它们,我尝试:

find /home -iname "*.pdf" | grep -v "access denied"

但是,结果是相同的。我如何摆脱这些界限?


Answers:


19

您尝试的操作无效,因为access denied输出是错误,并通过STDERR而不是通过管道发送到的STDOUT发送grep

您可以通过仅重定向STDERR来避免看到这些错误

find /home -iname "*.pdf" 2>/dev/null

或正如David Foerster所说,我们可以更简洁地关闭 STDERR

find /home -iname "*.pdf" 2>&-

但是,我怀疑您实际上只想搜索房屋而不是其他用户的房屋,所以也许您真的想要

find ~ -iname "*.pdf"

如果这引发了错误,则您的本地配置中可能存在一些错误的所有权,应对此进行调查。


3
Grrr,为什么人们总是以30秒击败我?:\
YoureAGitForNotUsingGit

找到:“ /home/ihsan/.gvfs”:访问被拒绝找到:“ /home/ihsan/.dbus”:访问被拒绝,对于命令
〜– solfish

这有什么问题吗?是的,我还希望其他用户的主目录也由我创建以进行测试
solfish

2
@solfish据我所知,这些文件应归您所有。您可能想要sudo chown $USER: ~/.gvfs ~/.dbus
-Zanna

1
以关闭stderr应该足够了2>&-。如果GNU find尝试将错误消息写入功能失常的文件描述符,则不会终止自身。对于所有权问题sudo chown -R $USER: ...,如果其中有更多文件不归所有权,则问题会更有效$USER
大卫·佛斯特

8

被拒绝的访问可能正在打印stderr而不是stdout

尝试这个:

find /home -iname "*.pdf" 2>&1 | grep -v "access denied"

2>&1重定向从输出stderrstdout,这样grep -v可以做自己的工作。(默认情况下,|仅管道stdout而不是管道stderr。)


但是对于这个2>&1意味着是否存在stderr发送到stdout?
solfish

@solfish是的,这才是重点:)
You'reAGitForNotUsingGit

我不明白的是在“ |”之前 作为输出; 我们只是stderr对吗?在“ |”之后 作为输入,我们得到了
solfish

@solfish好吧,大约一年半以前,我遇到了这个问题,并且能够使用其他方法来解决它。但是,然后在我的回答下面提出一条建议,建议仅使用2>&1...我不是bash专家,所以如果那是不正确的,请这样做:)
YoureAGitForNotUsingGit

@AndroidDev我建议将其他方法添加到此答案中作为替代方法。Etan Reisner的批评流程替代不是可移植的。但是bash在Ubuntu中有它,除了POSIX模式。我认为这是最好的解决方案-被恶意命名的文件access denied仍会出现。
伊莱亚·卡根

4

您可能是说“权限被拒绝”,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可能仍会返回一些有用的结果,并且似乎可以正常工作。例如,如果ac存在但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.13.3.2.23.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上面。

通过引入一个新的文件描述符来保持要交换的两个标准流之一,然后执行重定向以交换它们,并关闭新的文件描述符,可以起作用。

  • 文件描述符1是stdout,文件描述符2是stderr(未重定向的0是stdin)。但是您也可以使用其他文件描述符进行重定向。这可以用来打开或保持打开文件或设备。
  • 3>&1 将文件描述符3重定向到stdout,以便随后重定向stdout(文件描述符1)时,仍可以轻松地写入原始stdout。
  • 1>&2将stdout重定向到stderr。由于文件描述符3仍然是原始标准输出,因此仍可以对其进行访问。
  • 2>&3 将stderr重定向到文件描述符3,即原始stdout。
  • 3>&- 关闭不再需要的文件描述符3。
  • 有关更多信息,请参见如何管道stderr,而不是stdout?IO重定向-交换stdout和stderr(高级),尤其是仅将stderr通过过滤器进行管道传递

但是,此方法的缺点是将搜索结果发送到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的方法仍然可以使用。此外,当bashPOSIX模式运行时,将关闭对进程替换的支持。

bash在POSIX模式下运行的一种情况是将其调用为sh1。因此,在像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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.