我经常对这个简单的命令感到沮丧:
find / | fgrep somestuff.ext
当我不使用 时sudo
,我会得到一行又一行的权限被拒绝 - 这很公平,但是为什么当 grep 从管道读取它时,这个输出没有被忽略?
为什么这种形式的输出直接发送到终端窗口而不是传递到管道中(我怀疑一定会发生这种情况)并随后被 grep 忽略,而 cat 生成的相同行(假设我有权限被拒绝的消息存储在文本中) file) 会正确地进入管道并被我的 grep 模式忽略吗?
我觉得 STDIN/STDOUT 过程中有一些我不理解的地方
答案1
权限被拒绝的消息不会发送到 stdout,find
而是发送到 stderr。您可以将整个 stderr 重定向到位存储桶:
find 2>/dev/null | fgrep somestuff.ext
另外,要查找给定的文件,您不需要任何 grep:
find . -name somestuff.ext
您仍然可以应用2>/dev/null
.
要仅抑制权限被拒绝的消息,您可以使用
2> >(grep -v 'Permission denied' >&2)
在bash中。
答案2
虽然 choroba 的好答案解决了您的问题,但您注意到的行为的原因是 bash 中的默认管道行为(我想在大多数 shell 中也是如此)。
如管道部分所述man bash
:
command 的标准输出通过管道连接到command2 的标准输入。此连接在命令指定的任何重定向之前执行(请参阅下面的重定向)。
这意味着stderr
ofcommand1
默认情况下不会command2
通过管道馈送,而是被驱动到您的 tty,即默认的 stderr 链接。
Bash 手册还说:
如果使用 |&,command 的标准错误除了其标准输出之外,还会通过管道连接到 command2 的标准输入;它是 2>&1 | 的简写。
因此,在您的情况下,如果您想通过管道传输到 grep 命令,默认情况下发送给/dev/stderr
您的 find 命令错误需要使用以下两种形式之一:
find / |& fgrep somestuff.ext
find / 2>&1 | fgrep somestuff.ext
您的问题的标题也可以是“为什么管道忽略 stderr”。
答案是因为 bash 和 linux 就是这样默认的;stdout
与 不同地对待stderr
,以便用户能够以不同的方式记录/处理这两个输出。
例如,您可以通过管道将stdout
命令 1 传送到stdin
命令 2,同时您可以stderr
使用 . 将命令 1 发送到日志文件2>errorlog.txt
。
实际上,当您运行没有指定任何重定向的命令时,例如
find /
它相当于
find / 1>/dev/stdout 2>/dev/stderr
最终解决为:
find / 1>/dev/tty1 2>/dev/tty1 #assuming that you are logged in tty1
可以通过单个验证ls
:
ls -all /dev/st*
lrwxrwxrwx 1 root root 15 Nov 25 15:36 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Nov 25 15:36 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Nov 25 15:36 /dev/stdout -> /proc/self/fd/1
ls -all /proc/self/fd/2
lrwx------ 1 root root 64 Nov 28 02:46 /proc/self/fd/2 -> /dev/tty1
ls -all /proc/self/fd/1
lrwx------ 1 root root 64 Nov 28 02:46 /proc/self/fd/1 -> /dev/tty1
如果出于任何原因您想要“加入”stdout
命令stderr
,那么您需要明确声明bash
使用|&
(对于管道)或2>&1
(对于任何类型的输出重定向)的目的