在两个单独的流中显示stdout和stderr


13

我正在寻找一种在视觉上分离stdout和stderr的方法,以使它们不交错并且可以轻松识别。理想情况下,stdout和stderr在显示它们的屏幕上应具有单独的区域,例如在不同的列中。例如,输出看起来像这样:

~$ some command
some useful output info
ERROR: an error
more output
ERROR: has occurred
another message
~$ 

而是看起来像这样:

~$ some command          |
some useful output info  |
more output              |  ERROR: an error
another message          |  ERROR: has occurred
~$                       |


这个问题似乎并不是在问同样的事情,而且没有一个答案提供了这里要问的问题。
Michael Homer

2
将流重定向到两个不同的日志文件,然后在它们上使用诸如MultiTail之类的东西是否有用?vanheusden.com/multitail
库萨兰达

注释输出实用程序看起来有用吗,还是需要按列输出?
杰夫·谢勒

Answers:


5

您可以使用GNU screen的垂直拆分功能:

#! /bin/bash -
tmpdir=$(mktemp -d) || exit
trap 'rm -rf "$tmpdir"' EXIT INT TERM HUP

FIFO=$tmpdir/FIFO
mkfifo "$FIFO" || exit

conf=$tmpdir/conf

cat > "$conf" << 'EOF' || exit
split -v
focus
screen -t stderr sh -c 'tty > "$FIFO"; read done < "$FIFO"'
focus
screen -t stdout sh -c 'read tty < "$FIFO"; eval "$CMD" 2> "$tty"; echo "[Command exited with status $?, press enter to exit]"; read prompt; echo done > "$FIFO"'
EOF

CMD="$*"
export FIFO CMD

screen -mc "$conf"

用作例如:

that-script 'ls / /not-here'

这个想法是它运行带有临时conf文件的屏幕,该文件在垂直拆分布局中启动两个屏幕窗口。在第一个命令中,我们将stderr连接到第二个命令来运行您的命令。

我们在第二个窗口中使用命名管道将其tty设备与第一个窗口进行通信,并且在第一个窗口将命令完成时告知第二个窗口。

与基于管道的方法相比,另一个优点是命令的stdout和stderr仍连接到tty设备,因此它不会影响缓冲。两个窗格也可以独立地上下滚动(使用screen的复制模式)。

如果您bash使用该脚本以交互方式运行shell ,您会注意到提示将显示在第二个窗口中,而该shell将读取您在第一个窗口中键入的内容,因为这些shell在stderr上输出其提示。

对于bash,您键入的回显也将出现在第二个窗口中,因为该回显也是由bashstderr上的shell输出的(对于则为readline )。对于其他类似的Shell ksh93,它将显示在第一个窗口中(由终端设备驱动程序而不是Shell输出回显),除非您将Shell置于emacsvi模式下set -o emacsset -o vi


1

这是一个基于annotate-outputDebian ANNOTATE-OUTPUT(1)脚本的丑陋解决方案。不确定这是否是您要寻找的东西,但可能以以下内容开头:

#!/bin/bash 

readonly col=150 # column to start error output 

add_out ()
{
    while IFS= read -r line; do
        echo "$1: $line"
    done
    if [ ! -z "$line" ]; then
        echo -n "$1: $line"
    fi
}

add_err ()
{
    while IFS= read -r line; do
        printf "%*s  %s %s: %s\n" $col "|" "$1" "$line"
    done
    if [ ! -z "$line" ]; then
        printf "%*s %s: %s" $col "$1" "$line"
    fi
}

cleanup() { __st=$?; rm -rf "$tmp"; exit $__st; }
trap cleanup 0
trap 'exit $?' 1 2 13 15

tmp=$(mktemp -d --tmpdir annotate.XXXXXX) || exit 1
OUT=$tmp/out
ERR=$tmp/err

mkfifo $OUT $ERR || exit 1

add_out OUTPUT < $OUT &
add_err ERROR < $ERR &

echo "I: Started $@"
"$@" > $OUT 2> $ERR ; EXIT=$?
rm -f $OUT $ERR
wait

echo "I: Finished with exitcode $EXIT"

exit $EXIT

您可以使用./this_script another_script或进行测试command


1

我将尝试分析您的问题的以下部分:

而是看起来像这样:

 〜$一些命令
 一些有用的输出信息|
 更多输出| 错误:错误
 另一则讯息| 错误:发生
 〜$ 

如果要分解您想要的内容,请执行以下操作:

1)stdout流不会以a结束每一行,CR LF而是以'|结束。字符。当然,这不会使两个流对齐,并且对齐是stdout不可能的,因为它将必须预测添加到的将来行的长度,这当然是不可能的。

2)假设我们忘了对齐,那么我们将简单地在stderr经过在每行开头添加“ ERROR:”的管道处理后输出。我想通过制作一个简单的脚本并确保stderr总是通过此脚本得出来,这非常容易。

但这将创建如下输出:

〜$一些命令
 一些有用的输出信息|
 更多输出| 错误:错误
 另一则讯息| 错误:发生

哪个没有真正的帮助,是吗?我也不相信,这也是你在追求什么!

我认为第一个问题的问题是,您没有考虑到流中附加的每行的串行性质,而这是因为两个流都可以异步编写。

我相信,最可能的解决方案是使用ncurses
看到。
[ http://www.tldp.org/HOWTO/html_single/NCURSES-Programming-HOWTO/]
[ http://invisible-island.net/ncurses/ncurses-intro.html#updating]

为了完成您需要做的事情,您需要缓冲两个流并将它们组合以产生第三个缓冲区,该缓冲区从两个缓冲区中获取元素。然后,通过擦除终端屏幕并在每次第三缓冲区更改时重新粉刷它,将第三缓冲区转储到终端屏幕中。但这是ncurses可行的方式,那么为什么要重新发明轮子而不从那里开始呢?
在任何情况下,您都必须接管终端屏幕完全绘制的方式!并根据需要在屏幕的重新打印版本中重新对齐文本。很像带有终端角色的电子游戏。
我希望我的回答将有助于阐明您追求的目标的局限性...
对不起,请重复我的内容,但是您所显示的最大问题是stdoutstderr流的“处理器”将如何提前知道添加到其中的将来行的长度,以便正确对齐它们。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.