将标准err发送到管道 [英] Send standard err to pipe

查看:172
本文介绍了将标准err发送到管道的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#!/usr/bin/perl
print "OUT 1\n";
print STDERR "ERR 1\n";
print "OUT 2\n";
print STDERR "ERR 2\n";

运行时:

lorkenpeist$ ./testscript.pl
OUT 1
ERR 1
OUT 2
ERR 2

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

#!/bin/awk -f
{print "STDERR: " $0}

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

lorkenpeist$ ./testscript.pl | ./stderr.awk
ERR 1
ERR 2
STDERR: OUT 1
STDERR: OUT 2

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

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

lorkenpeist$ ./testscript.pl 2>&1 | ./stderr.awk
STDERR: ERR 1
STDERR: ERR 2
STDERR: OUT 1
STDERR: OUT 2

不仅所有内容都由stderr.awk处理,而不仅仅是标准err,而且打印语句的顺序也没有保留.有什么方法可以只将标准err发送到stderr.awk,并保留打印语句的顺序?我真正想看到的是:

OUT 1
STDERR: ERR 1
OUT 2
STDERR: ERR 2

我开始怀疑IO重定向根本不能解决问题,但是我对替代品一无所知.

鉴于标准输出是经过缓冲的,而标准err没有,因此,我似乎无法完全控制打印语句在终端上的显示顺序.话虽如此,我宁愿如果订单至少被保留下来,而不是在任何标准输出之前打印所有标准err.另外,有没有办法使标准输出和/或管道不被缓冲?

解决方案

Andy Lutomirski ,我认为如何通过管道传递stderr而不是stdout 会有所帮助.

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

( ./genouterr.sh 2>&1 1>&3 | sed 's/^/STDERR: /' >&2) 3>&1

那是什么:

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

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

请注意,由于缓冲等原因,命令的输出与标准输出和标准错误的交织可能与它们在终端屏幕上的显示方式有所不同,而无需任何I/O重定向.这几乎是不可避免的.如果要避免这种情况,您会参与使用伪tty(ptys)(这是一个复杂的领域),即使您需要通过sed过滤某些内容,我也不想保证这种行为. /p>

脚本genouterr.sh可以简单地生成标准输出和标准错误行:

#!/bin/bash
for i in {01..50}
do
  echo "stdout $i"
  echo "stderr $i" >&2
done

我做的原始测试(我在一两个月前写的,作为一个练习来扩展另一个问题)正在寻找包含以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

运行此脚本时,发现文件out.1为空,out.3包含genouterr.sh写入标准输出的内容,而out.2包含genouterr.sh写入标准错误的内容的过滤版本.

I am trying to write a simple script that takes standard output and standard err and puts the word STDERR: at the beginning of each line of standard err. For testing I have a simple script that outputs a couple lines alternating between standard out and standard err:

#!/usr/bin/perl
print "OUT 1\n";
print STDERR "ERR 1\n";
print "OUT 2\n";
print STDERR "ERR 2\n";

When I run it:

lorkenpeist$ ./testscript.pl
OUT 1
ERR 1
OUT 2
ERR 2

And here is my script stderr.awk to add STDERR:

#!/bin/awk -f
{print "STDERR: " $0}

If I run ./testscript.pl | ./stderr.awk (which is obviously wrong because I'm piping standard out instead of standard err):

lorkenpeist$ ./testscript.pl | ./stderr.awk
ERR 1
ERR 2
STDERR: OUT 1
STDERR: OUT 2

I see that standard err is output immediately, while standard output is delayed because of the pipe. The original order of the print statements is not preserved.

I can also redirect standard err to standard output:

lorkenpeist$ ./testscript.pl 2>&1 | ./stderr.awk
STDERR: ERR 1
STDERR: ERR 2
STDERR: OUT 1
STDERR: OUT 2

Not only is everything processed by stderr.awk instead of just standard err, but again the order of the print statements is not preserved. Is there any way to send just the standard err to stderr.awk, and also preserve the order of the print statements? What I'd really like to see is:

OUT 1
STDERR: ERR 1
OUT 2
STDERR: ERR 2

I'm beginning to suspect that IO redirection simply isn't the answer, but I'm at a loss for alternatives.

EDIT: Given that standard output is buffered and standard err is not, It looks like I don't have complete control over the order in which the print statements appear on the terminal. That being said, I would prefer if order was at least somewhat preserved, instead of all of standard err being printed before any of standard output. Alternatively, is there a way to make standard output and/or pipes unbuffered?

解决方案

Like Andy Lutomirski, I think that How to pipe stderr and not stdout can help.

Here's a command sequence that filters standard error without changing standard output; ./genouterr.sh generates information on both standard output and standard error.

( ./genouterr.sh 2>&1 1>&3 | sed 's/^/STDERR: /' >&2) 3>&1

What that does is:

  • Run a subshell (...) and sends it output from file descriptor 3 to standard output (3>&1).
  • In the subshell, it runs the command ./genouterr.sh, piping its standard output to sed.
  • But, before running the shell script, it arranges for error output to go to standard output (2>&1), and for standard output to go to file descriptor 3 (1>&3). Thus, anything written to standard output by the script goes to file descriptor 3 in fact, while anything written to standard error by the script goes down the pipe.
  • The RHS of the pipeline is sed, inserting STDERR: at the start of each line, and then redirecting its standard output to standard error (1>&2).

The net result is that what ./genouterr.sh writes on standard output appears on standard output; what ./genouterr.sh writes on standard error is prefixed with STDERR: and appears on standard error.

Note that because of buffering and so on, the output from the commands may interleave standard output and standard error differently from the way they'd appear on the terminal screen without any I/O redirection. This is pretty close to unavoidable. If you are going to avoid it, you get involved in using pseudo-ttys (ptys), which is a complex area, and I'd not want to guarantee the behaviour even then if you need to filter something through sed.

The script genouterr.sh simply generates both standard output and standard error lines:

#!/bin/bash
for i in {01..50}
do
  echo "stdout $i"
  echo "stderr $i" >&2
done

And the original test I did (I wrote this a month or two ago, as an exercise extending the other question) was looking for standard error lines containing a number ending in 0 (using grep); changing that to a sed script is a work of moments.

#!/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

When you run this script, you find that the file out.1 is empty, out.3 contains what genouterr.sh wrote to standard output, and out.2 contains the filtered version of what genouterr.sh wrote to standard error.

这篇关于将标准err发送到管道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆