[unix] 두 개의 별도 스트림으로 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
~$                       |



답변

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

#! /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에서 프롬프트를 출력합니다.

의 경우 bash1, 에코 당신은 또한과 두 번째 창에 나타납니다 입력 한 내용의 에코 (의 경우 readline에 쉘에 의해 출력 bash표준 오류에는)뿐만 아니라. 다른 쉘 등으로 ksh93, 그것은 첫 번째 창에 (표시됩니다 에코 당신이 쉘을 넣어하지 않는 한, 상기 단말 장치 드라이버에 의해 출력이 아닌 쉘) emacs또는 vi와 모드 set -o emacsset -o vi.


답변

이것은 annotate-outputDebian 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.


답변

나는 당신의 질문의 다음 부분을 분석하려고 노력할 것입니다 :

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

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

원하는 것을 분해하고 싶다면 :

1) stdout스트림은 각 줄을 a로 끝내지 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스트림 의 “프로세서”가 미래 라인의 길이를 미리 정렬하여 올바르게 정렬하는 방법입니다.


답변