将标准错误发送到管道

Lor*_*ist 1 unix io-redirection

我正在尝试编写一个简单的脚本,该脚本采用标准输出和标准错误,并将该单词放在STDERR:标准错误每行的开头。为了进行测试,我有一个简单的脚本,它在标准输出和标准错误之间交替输出几行:

#!/usr/bin/perl
print "OUT 1\n";
print STDERR "ERR 1\n";
print "OUT 2\n";
print STDERR "ERR 2\n";
Run Code Online (Sandbox Code Playgroud)

当我运行它时:

lorkenpeist$ ./testscript.pl
OUT 1
ERR 1
OUT 2
ERR 2
Run Code Online (Sandbox Code Playgroud)

这是我stderr.awk要添加的脚本STDERR:

#!/bin/awk -f
{print "STDERR: " $0}
Run Code Online (Sandbox Code Playgroud)

如果我运行./testscript.pl | ./stderr.awk(这显然是错误的,因为我正在输出标准而不是标准错误):

lorkenpeist$ ./testscript.pl | ./stderr.awk
ERR 1
ERR 2
STDERR: OUT 1
STDERR: OUT 2
Run Code Online (Sandbox Code Playgroud)

我看到标准错误是立即输出的,而标准输出由于管道而被延迟。打印语句的原始顺序不会保留。

我还可以将标准错误重定向到标准输出:

lorkenpeist$ ./testscript.pl 2>&1 | ./stderr.awk
STDERR: ERR 1
STDERR: ERR 2
STDERR: OUT 1
STDERR: OUT 2
Run Code Online (Sandbox Code Playgroud)

不仅所有内容都被处理,而stderr.awk不仅仅是标准错误,而且打印语句的顺序也没有被保留。有没有办法只将标准错误发送到stderr.awk,并保留打印语句的顺序?我真正想看到的是:

OUT 1
STDERR: ERR 1
OUT 2
STDERR: ERR 2
Run Code Online (Sandbox Code Playgroud)

我开始怀疑 IO 重定向根本不是答案,但我找不到其他选择。

编辑: 鉴于标准输出是缓冲的而标准错误不是,看起来我无法完全控制打印语句在终端上出现的顺序。话虽如此,我希望至少在某种程度上保留顺序,而不是在任何标准输出之前打印所有标准错误。或者,有没有办法使标准输出和/或管道无缓冲?

Jon*_*ler 5

Andy Lutomirski一样,我认为如何管道 stderr 而不是 stdout可以提供帮助。

这是一个过滤标准错误而不更改标准输出的命令序列;./genouterr.sh生成有关标准输出和标准错误的信息。

( ./genouterr.sh 2>&1 1>&3 | sed 's/^/STDERR: /' >&2) 3>&1
Run Code Online (Sandbox Code Playgroud)

其作用是:

  • 运行子 shell(...)并将其输出从文件描述符 3 发送到标准输出 ( 3>&1)。
  • 在子 shell 中,它运行命令./genouterr.sh,将其标准输出通过管道传输到sed
  • 但是,在运行 shell 脚本之前,它会将错误输出安排到标准输出 ( 2>&1),并将标准输出安排到文件描述符 3 ( 1>&3)。因此,脚本写入标准输出的任何内容实际上都会进入文件描述符 3,而脚本写入标准错误的任何内容都会进入管道。
  • 管道的右侧为sed,在每行的开头插入STDERR:,然后将其标准输出重定向到标准错误 ( 1>&2)。

最终结果是./genouterr.sh标准输出上写入的内容出现在标准输出上;标准错误上写入的内容./genouterr.sh以 为前缀STDERR:并出现在标准错误上。

请注意,由于缓冲等原因,命令的输出可能会交错标准输出和标准错误,这与它们在没有任何 I/O 重定向的情况下在终端屏幕上显示的方式不同。这几乎是不可避免的。如果你想避免它,你就会涉及到使用伪 ttys (ptys),这是一个复杂的领域,即使这样,如果你需要通过sed.

该脚本genouterr.sh只是生成标准输出和标准错误行:

#!/bin/bash
for i in {01..50}
do
  echo "stdout $i"
  echo "stderr $i" >&2
done
Run Code Online (Sandbox Code Playgroud)

我所做的原始测试(我在一两个月前写的,作为扩展另一个问题的练习)是寻找包含以 0 结尾的数字的标准错误行(使用grep);将其更改为sed脚本是一个瞬间的工作。

#!/bin/bash
set -x
rm -f out.[123]
./genouterr.sh 1>/dev/null
./genouterr.sh 2>/dev/null
( ./genouterr.sh 2>&1 1>&3 | grep '[0-9]0' >&2) 3>out.3 2>out.2 1>out.1
ls -l out.[123]
( ./genouterr.sh 2>&1 1>&3 | grep '[0-9]0' >&2) 3>&1
Run Code Online (Sandbox Code Playgroud)

当您运行此脚本时,您会发现该文件out.1是空的,包含写入标准输出的out.3内容,并包含写入标准错误的内容的过滤版本。genouterr.shout.2genouterr.sh