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

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

我正在寻找一种在视觉上分离 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
~$                       |

答案1

您可以使用 GNUscreen的垂直分割功能:

#! /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复制模式)。

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

在 的情况下bash回声您输入的内容也会显示在第二个窗口上回声bash也是由 shell(在 的情况下为 readline)在 stderr 上输出。对于其他一些 shell,例如ksh93,它将显示在第一个窗口上(回声由终端设备驱动程序而不是 shell 输出),除非您使用or将 shell 置于emacsorvi模式。set -o emacsset -o vi

答案2

这是一个基于annotate-outputDebian 脚本的丑陋解决方案注释输出(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

答案3

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

相反,看起来像这样:

~$ 一些命令
 一些有用的输出信息 |
 更多输出 |错误:一个错误
 另一条消息|错误:已发生
 〜$

如果有人想分解你想要的是:

1)stdout流不会以“|”结束每一行,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

相关内容