我正在寻找一种在视觉上分离 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 置于emacs
orvi
模式。set -o emacs
set -o vi
答案2
这是一个基于annotate-output
Debian 脚本的丑陋解决方案注释输出(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
,那么为什么要重新发明轮子而不是从那里开始呢?
无论如何,你都必须完全接管终端屏幕的绘制方式!并根据需要重新对齐屏幕重印版本中的文本。很像带有终端角色的视频游戏。
我希望我的回答有助于澄清您所追求的限制...请原谅我重复这一点,但您所展示的最大问题是 和流
的“处理器”如何提前知道 的长度添加未来的线条以便正确对齐它们。stdout
stderr