仅通过过滤器管道STDERR

Mar*_*llo 70 linux bash redirect

在使用STDOUT统一它之前,有没有办法在bash中通过过滤器管道STDERR?那就是我想要的

STDOUT ?????????????????
                       ??????> terminal/file/whatever
STDERR ?? [ filter ] ???
Run Code Online (Sandbox Code Playgroud)

而不是

STDOUT ?????
           ?????[ filter ]???> terminal/file/whatever
STDERR ?????
Run Code Online (Sandbox Code Playgroud)

Pau*_*bel 61

这是一个例子,模仿如何在bash中交换文件描述符.a.out的输出如下,没有'STDXXX:'前缀.

STDERR: stderr output
STDOUT: more regular

./a.out 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
more regular
stdErr output
Run Code Online (Sandbox Code Playgroud)

引用上面的链接:

  1. 首先将stdout保存为&3(&1被欺骗为3)
  2. 接下来将stdout发送到stderr(&2被骗到1)
  3. 发送stderr到&3(stdout)(&3被骗到2)
  4. 关闭&3(& - 被欺骗3)

  • 这适用于任何POSIX shell,而不仅仅是Bash. (4认同)
  • **注意**:这假设 FD 3 未在使用中,并且不会撤消文件描述符 1 和 2 的交换,因此您不能继续将其传递给另一个命令。有关更多详细信息和解决方法,请参阅 [此答案](/sf/answers/3680264941/)。有关 {ba,z}sh 更简洁的语法,请参阅 [此答案](/sf/answers/3680256121/)。 (3认同)
  • 附加参考:[BashFAQ / 047](http://mywiki.wooledge.org/BashFAQ/047) (2认同)
  • 它对我不起作用。最后我让它与`3>&2 2>&1 1>&3-`一起工作。 (2认同)

sol*_*ack 24

过程替换的天真使用似乎允许stderr单独过滤stdout:

:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err
Run Code Online (Sandbox Code Playgroud)

需要注意的是stderr出来上stderrstdoutstdout,我们可以在另一个子shell包裹了整个事情,并重定向到文件看到oe

( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e
Run Code Online (Sandbox Code Playgroud)

  • 好的,但你可以省略:; 这条线也可以复制 - 给我:; 看起来不像一个提示,它没有使示例更清晰(从输出中分离命令)但令人困惑.虽然我理解你的观点,但我们不讨论语法/惯例. (20认同)
  • 有些人使用“$”作为命令提示符。然后他们经常编写 shell 示例,例如:`$ cat /var/log/syslog | fgrep ...`。然而,由于“$”,该行不可复制粘贴。`:;` 看起来像一个提示符,但基本上是一个 shell 无操作;这样您就可以安全地选择并粘贴整行。 (2认同)

Tom*_*ale 14

TL; DR:

$ cmd 2> >(stderr-filter >&2)
Run Code Online (Sandbox Code Playgroud)

例:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
Run Code Online (Sandbox Code Playgroud)

这将适用于bash和zsh.Bash现在几乎无处不在,但是,如果你真的需要一个(非常粗糙)POSIX的解决方案sh,那么请看这里.


说明

到目前为止,最简单的方法是通过进程替换重定向STDERR :

进程替换允许使用文件名引用进程的输入或输出.它采取的形式

>(list)
Run Code Online (Sandbox Code Playgroud)

进程列表以异步方式运行,其输入或输出显示为文件名.

所以你用过程替换得到的是一个文件名.

就像你能做的那样:

$ cmd 2> filename
Run Code Online (Sandbox Code Playgroud)

你可以做

$ cmd 2> >(filter >&2)
Run Code Online (Sandbox Code Playgroud)

>&2重定向的filter的STDOUT回到原来的STDERR.

  • 这个答案有一个警告:bash _不等待替换的进程完成_,而 FD 则在交换 1 和 2 时等待。这可能很重要。在作为 systemd 服务运行的脚本中执行 `exec 2> >(while .. read .. echo)` 时,我感到很焦灼。Journald 捕获服务的 fd2,并从“<N>”前缀推断日志级别:在输出行前面添加“<2>”,然后您将获得分配给日志记录的错误级别。但有时 systemd 会在主进程退出时更快地杀死整个进程组,然后它才能记录最后一条、最重要的消息! (3认同)
  • 不错的简单解决方案。警告:小心地将其用作 cron 作业的一部分;进程替换在 cron 使用的某些 shell 中不可用。 (3认同)

Tom*_*ale 13

TL; DR:(bash和zsh)

$ cmd 2> >(stderr-filter >&2)
Run Code Online (Sandbox Code Playgroud)

例:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
Run Code Online (Sandbox Code Playgroud)

StackExchange网络上的许多答案都具有以下形式:

cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
Run Code Online (Sandbox Code Playgroud)

这有一个内置的假设:文件描述符3并未用于其他用途。

而是使用命名文件描述符,{ba,z}sh并将分配下一个可用文件描述符> = 10:

cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'
Run Code Online (Sandbox Code Playgroud)

请注意,POSIX不支持命名文件描述符sh

上面的另一个问题是,如果不再次将STDOUT和STDERR交换回其原始值,则无法将该命令传递给其他命令。

为了允许在POSIX中继续传送管道sh(并且仍然假设未使用FD 3),它变得很复杂

(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1
Run Code Online (Sandbox Code Playgroud)

因此,鉴于此假设和陈旧的语法,使用上面的TL; DR中显示的更简单的bash/ zsh语法并在此处进行解释可能会更好。


小智 8

我发现使用bash过程替换更容易记住和使用,因为它几乎逐字地反映了原始意图.例如:

$ cat ./p
echo stdout
echo stderr >&2
$ ./p 2> >(sed -e 's/s/S/') | sed 's/t/T/'
sTdout
STderr
Run Code Online (Sandbox Code Playgroud)

使用第一个sed命令作为stderr的过滤器,第二个sed命令用于修改连接的输出.

请注意,2>之后的空格对于正确解析命令是必需的.


sig*_*ine 5

Advanced Bash Scripting Guide 的这一页的最后一部分是"仅将stderr重定向到管道".

#仅将stderr重定向到管道.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.
Run Code Online (Sandbox Code Playgroud)

#谢谢,SC

这可能就是你想要的.如果没有,ABSG的其他部分应该能够帮助你,这是非常好的.