如何在 stderr 和 stdout 之间插入换行符

如何在 stderr 和 stdout 之间插入换行符

这个问题帮助我理解如何结合 stderr 和 stdout:

如何将 stderr 重定向到文件

使用以下命令:

gunzip -vc /opt/minecraft/wonders/logs/20* 2>&1

但是如何在两者之间插入换行符,以便输出可以出现在不同的行上而不是聚集在一起?

stderr 首先出现,它始终只有 1 行,如下所示:

/opt/minecraft/wonders/logs/2017-08-28-2.log.gz:

而标准输出通常(但不总是)是多行,例如:

06:17:05: Starting minecraft server version 1.10.2
06:18:21: Loading properties
06:18:21: Default game type: SURVIVAL
06:18:21: Generating keypair
06:18:21: Starting Minecraft server on *:25565
06:18:22: Using default channel type

但是因为它们之间没有换行符,所以每个要解压缩的文件的第一行总是这样的:

/opt/minecraft/wonders/logs/2018-08-18-2.log.gz:    06:17:05: Starting minecraft server version 1.10.2

我需要将这两部分放在不同的行上,这样我就可以用 grep 过滤它们。但我还希望相应的 stdout 直接跟在其 stderr(其文件名)后面,这样我就知道它来自哪个文件。像这样:

/opt/minecraft/wonders/logs/2018-08-18-2.log.gz:    
06:17:05: Starting minecraft server version 1.10.2
06:18:21: Loading properties
06:18:21: Default game type: SURVIVAL
06:18:21: Generating keypair
06:18:21: Starting Minecraft server on *:25565
06:18:22: Using default channel type

这是我通过 grep 传递的命令:

egrep -iv "^\[.*(stopping|starting|saving|keep|spawn|joined|lost|left|: (<.*>|\/)|\/(help|forge))

其目的是从日志文件中删除某些行,保留相关的行。

答案1

这里有一个简单的方法可以做到这一点,假设这个命令:

ls -d Documents/ NotExist/ /dev/null

输出为:

ls: cannot access 'NotExist/': No such file or directory
/dev/null  Documents/

第一行是stderr,第二行是stdout,如果我将输出传输到,xargs我可以在之后打印一个新行stderr,然后打印stdout

ls -d Documents/ NotExist/ /dev/null | xargs echo -e '\n'

这给了我:

ls: cannot access 'NotExist/': No such file or directory

 /dev/null Documents/

答案2

复制文件描述符是可能的。长话短说:管道 stderr,可以在那里处理它,并将 stdin 发送到控制终端(简写为/dev/tty),以便它仍然显示在屏幕上:

$ stat /etc/passwd noexist 2>&1 > /dev/tty | while IFS= read -r line; do echo "---"; echo "$line"; echo "---"; done
  File: /etc/passwd
  Size: 2380        Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d  Inode: 937653      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-08-20 13:05:01.171247259 +0800
Modify: 2018-07-24 04:51:36.799057737 +0800
Change: 2018-07-24 04:51:36.867020035 +0800
 Birth: -
---
stat: cannot stat 'noexist': No such file or directory
---

这里我将 stderr 与---line 分开,但你明白我的意思。

另一种方法是,如果你想在视觉上区分它们,那就是给输出着色。基于Balazs Pozar 的回答,你可以这样做:

command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)

在此处输入图片描述


为了解决这个问题的编辑,如果您需要使用 stdout 和 stderr 进行过滤grep(例如,如果您在 中寻找一个模式stdout,但在 中寻找另一个模式stderr),则可以使用多个过程替换>()

gunzip -vc /opt/minecraft/wonders/logs/20* > >(grep 'stdin pattern' ) 2> >(grep 'stderrpattern' )

针对评论,如果您只需要将文件名/opt/minecraft/wonders/logs/2017-08-28-2.log.gz:与进一步的内容分开,则可以使用sed

gunzip -vc /opt/minecraft/wonders/logs/20* | 
sed -r 's/^(\/opt\/minecraft\/wonders\/logs\/2018-08-18-2.log.gz:) *(.*)$/\1\n\2/'

测试:

$ echo /opt/minecraft/wonders/logs/2018-08-18-2.log.gz:    06:17:05: Starting minecraft server version 1.10.2 | sed -r 's/^(\/opt\/minecraft\/wonders\/logs\/2018-08-18-2.log.gz:) *(.*)$/\1\n\2/'
/opt/minecraft/wonders/logs/2018-08-18-2.log.gz:
06:17:05: Starting minecraft server version 1.10.2

答案3

我认为实际发生的情况是 stdout 正在混入stderr,因为前者默认是行缓冲的,而后者默认是非缓冲的。举例来说:

$ gunzip -vc ../foo.txt.gz
../foo.txt.gz:  some text here
more text here
 26.7%

(请注意,这-v会导致在 stderr 上发出两条信息:文件名和 % 压缩 - 标准输出介于两者之间)。

如果您也对 stderr 进行行缓冲,则流将出现在单独的行上:

$ stdbuf -eL gunzip -vc ../foo.txt.gz
some text here
more text here
../foo.txt.gz:   26.7%

但不一定按照您需要的顺序 - 您可以尝试使用该sponge实用程序来吸收全部在输出之前,stdout 会先保留,这样 stderr 输出(文件名和%)就会首先出现在终端中:

gunzip -vc ../foo.txt.gz | sponge
../foo.txt.gz:   26.7%
some text here
more text here

相关内容