두 개의 별도 스트림에 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

screenGNU 의 수직 분할 기능을 사용할 수 있습니다 .

#! /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프롬프트가 두 번째 창에 표시되는 반면, 쉘은 stderr에 프롬프트를 출력할 때 첫 번째 창에 입력한 내용을 읽습니다.

의 경우 bash,에코입력한 내용이 두 번째 창에도 표시됩니다.에코stderr의 쉘( 의 경우 readline bash)에서도 출력됩니다. 와 같은 다른 쉘을 사용하면 ksh93첫 번째 창에 표시됩니다(에코또는 를 사용하여 셸을 emacs또는 vi모드로 설정하지 않는 한 셸이 아닌 터미널 장치 드라이버에 의해 출력됩니다 .set -o emacsset -o vi

답변2

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.

답변3

귀하의 질문 중 다음 부분을 분석해 보겠습니다.

대신 다음과 같이 보일 것입니다:

~$ 일부 명령
 유용한 출력 정보 |
 더 많은 출력 | 오류: 오류
 다른 메시지 | 오류: 발생했습니다.
 ~$

원하는 것을 분석하고 싶다면 다음과 같습니다.

1) 스트림은 각 줄을 a로 끝나지 않고 대신 '|'로 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스트림의 "프로세서"가 스트림의 길이를 미리 알 수 있는 방법입니다. 적절하게 정렬하기 위해 향후 행이 추가됩니다.

관련 정보