【问题标题】:Pipe only STDERR through a filter仅通过过滤器管道 STDERR
【发布时间】:2011-04-06 19:03:26
【问题描述】:

在 bash 中,有什么方法可以在将 STDERR 与 STDOUT 统一之前通过过滤器对其进行管道传输?也就是说,我想要

STDOUT ────────────────┐
                       ├─────> terminal/file/whatever
STDERR ── [ filter ] ──┘

而不是

STDOUT ────┐
           ├────[ filter ]───> terminal/file/whatever
STDERR ────┘

【问题讨论】:

标签: linux bash redirect


【解决方案1】:

TL;DR:(bash 和 zsh)

$ cmd 2> >(stderr-filter >&2)

例子:

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

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

cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'

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

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

cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'

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

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

为了允许在 POSIX sh 中继续管道,(并且仍然假设不使用 FD 3)它 gets complicated

(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1

因此,鉴于此假设和粗糙的语法,您可能最好使用上面 TL;DR 中显示的更简单的 bash/zsh 语法,并解释了 here

【讨论】:

  • 为什么要两次发布相同的答案?
【解决方案2】:

TL;DR:

$ cmd 2> >(stderr-filter >&2)

例子:

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

这适用于 bash 和 zsh。如今,Bash 几乎无处不在,但是,如果您确实需要 POSIX 的(非常粗糙的)解决方案sh,那么see here


说明

到目前为止,最简单的方法是通过 process substitution 重定向 STDERR:

进程替换允许使用文件名来引用进程的输入或输出。它的形式是

>(list)

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

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

就像你可以做的那样:

$ cmd 2> filename

你可以的

$ cmd 2> >(filter >&2)

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

【讨论】:

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

高级 Bash 脚本指南this page 的最后一部分是“仅将 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.

# 谢谢,S.C.

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

【讨论】:

  • 我们有点犹豫是否推荐 ABSG 作为参考,因为它混合了文档、处方和意见,没有明确标记差异。有些部分也有可疑的内容,虽然你链接到的那个看起来不错。
【解决方案4】:

这是一个示例,以 how to swap file descriptors in 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

引用以上链接:

  1. 首先将标准输出另存为 &3(&1 被复制到 3 中)
  2. 接下来将 stdout 发送到 stderr(&2 被复制到 1 中)
  3. 将 stderr 发送到 &3 (stdout)(&3 被复制到 2 中)
  4. 关闭 &3(&- 被骗成 3)

【讨论】:

  • 它对我不起作用。最后我让它与3>&2 2>&1 1>&3-一起工作。
  • 注意:假设 FD 3 未在使用中,并且不会撤消文件描述符 1 和 2 的交换,因此您不能继续通过管道传输到另一个命令。有关更多详细信息和解决方法,请参阅this answer。如需更简洁的 {ba,z}sh 语法,请参阅 this answer
【解决方案5】:

对进程替换的幼稚使用似乎允许将stderrstdout 分开过滤:

:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err

注意stderr 出现在stderrstdout 上出现在stdout,我们可以通过将整个内容包装在另一个子shell 中并重定向到文件oe 来看到这一点

( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e

【讨论】:

  • 为什么 :;一开始?我试过没有魔法线,但似乎没有什么不同。
  • 有些人使用$ 作为命令提示符。然后他们经常编写 shell 示例,例如:$ cat /var/log/syslog | fgrep ...。但是,由于$,此行不可复制粘贴。 :; 看起来像一个提示,但基本上是一个 shell 无操作;这样您就可以安全地选择并粘贴整行。
  • 好的,但是你可以省略 :;而且这条线也可以复制粘贴:)对我来说:;看起来不像提示,它并没有使示例更清晰(将命令与输出分开),而是令人困惑。虽然我理解你的观点,我们不讨论语法/约定。
【解决方案6】:

我发现使用 bash 进程替换更容易记住和使用,因为它几乎一字不差地反映了初衷。例如:

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

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

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

【讨论】:

    【解决方案7】:

    看看命名管道:

    $ mkfifo err
    $ cmd1 2>err |cat - err |cmd2
    

    【讨论】:

    • 不会cat - err打破stdout和stderr的穿插吗?
    • @Martin - 视情况而定。如果 cmd1、cat、 cmd2 缓冲输出,那么您可能会看到输出乱序。
    • 这不会运行。 shell 无法完成打开 err 以进行写入,直到 cat 打开它以进行读取,但 cat 在完成读取标准输入之前不会打开它,直到 cmd1 死亡,但 @987654328 才能打开它@ 在完成打开 err 之前无法启动。这是一个僵局。 fifo(7) 记录了这一点:“通常,打开 FIFO 块直到另一端也打开。”
    猜你喜欢
    • 1970-01-01
    • 2020-10-24
    • 2013-03-14
    • 2011-05-03
    • 2017-11-29
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多