Я ищу способ визуально разделить 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
Вы можете использовать screen
функцию вертикального разделения GNU:
#! /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'
Идея в том, что он запускает screen с временным файлом conf, который запускает два окна screen в вертикальном разделенном макете. В первом мы запускаем вашу команду с stderr, подключенным ко второму.
Мы используем именованный канал для второго окна, чтобы соединить его tty-устройство с первым, а также для того, чтобы первое окно сообщало второму о завершении команды.
Другим преимуществом по сравнению с подходами на основе конвейера является то, что stdout и stderr команды по-прежнему подключены к устройствам tty, поэтому это не влияет на буферизацию. Обе панели также можно прокручивать вверх и вниз независимо (используя screen
режим копирования ).
Если вы запустите оболочку в bash
интерактивном режиме с помощью этого скрипта, вы заметите, что приглашение будет отображаться во втором окне, в то время как оболочка будет считывать то, что вы вводите в первом окне, поскольку эти оболочки выводят свое приглашение в stderr.
В случае с bash
,эхотого, что вы наберете, также появится во втором окне, как этоэховыводится оболочкой (readline в случае bash
) на stderr. С некоторыми другими оболочками, такими как ksh93
, он будет отображаться в первом окне (эховывод драйвера терминального устройства, а не оболочки), если только вы не переведете оболочку в режим emacs
или с помощью или .vi
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
знать длину будущих строк, добавляемых к нему, чтобы правильно их выровнять.