Отображение 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

Вы можете использовать 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или с помощью или .viset -o emacsset -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знать длину будущих строк, добавляемых к нему, чтобы правильно их выровнять.

Связанный контент