화면에 stderr 만 표시하지만 stdout과 stderr을 모두 파일에 기록


24

이것을 달성하기 위해 BASH 매직을 어떻게 사용할 수 있습니까?
화면에서 stderr 출력
만보 고 싶지만 stdout과 stderr을 파일에 쓰려고합니다.

설명 : stdout과 stderr이 모두 같은 파일로 끝나기를 원합니다. 순서대로 발생합니다.
불행히도 아래 답변 중 어느 것도이 작업을 수행하지 않습니다.


2
동일한 근본적인 문제에 대한 새로운 시도
Gilles 'SO- 악마 그만'

답변:


14

리디렉션이 없거나 아무것도없는 경우에도 >logfile 2>&1생성 순서대로 출력이 표시되는 것은 아닙니다.

우선, 애플리케이션의 stdout은 라인 버퍼링 (tty) 또는 버퍼링 (파이프 라인)되지만 stderr는 버퍼링되지 않으므로 독자와 관련하여 출력 순서 간의 관계가 끊어집니다. 파이프 라인의 후속 단계에서는 두 스트림에 결정적으로 액세스 할 수 없습니다 (개념적으로 병렬로 발생하는 일이며 항상 스케줄러에 종속됩니다-독자가 시간이 지남에 따라 작가가 이미 조각을 얻었을 경우) 두 파이프 모두에 쓰면 어느 쪽이 먼저 왔는지 알 수 없습니다).

"그들이 주문한 순서"는 실제로 응용 프로그램에 알려져 있습니다. stdout / stderr에서 출력 순서는 잘 알려진 고전적인 문제 일 수 있습니다.


7

당신이 건축 사용하는 경우 : 1>stdout.log 2>&1모두 표준 에러표준 출력 파일에 GET 리디렉션을하기 때문에 표준 출력을 재 지정은 이전에 설정되어 열려진 리디렉션.

순서를 반대로 바꾸면 stdout 이 파일로 리디렉션 된 다음 stderrstdout으로 복사 하여 파이프 할 수 있습니다 tee.

$ cat test
#!/bin/sh
echo OUT! >&1
echo ERR! >&2

$ ./test 2>&1 1>stdout.log | tee stderr.log
ERR!

$ cat stdout.log
OUT!

$ cat stderr.log
ERR!

두 스트림을 동일한 파일에 기록 할 수 없습니다.
artistoex

1
@ artistoex : tee -a동일한 파일에서 1>두 출력을 합산하는 데 사용되는 동일한 파일과 함께 사용할 수 있습니다 . : 같은 뭔가./test 2>&1 1>out+err.log | tee -a out+err.log
mmoya

나는 왜 그런지 모른다. 그러나 이것은 wget -O - www.google.de나를 위해 작동하지 않는다 . (아래 솔루션과 비교)
artistoex

4
사용하여 tee -a안정적으로 작동하지 않습니다 것은 아무것도 리디렉션되지 않은 경우 콘솔에있을 것 같은 출력을 가지고 있습니다. 통과 tee하는 출력은 명령에서 직접 오는 출력과 비교하여 약간 지연 될 수 있으므로 나중에 로그에 표시 될 수 있습니다.
Gilles 'SO- 악마 그만해'

이것은 나를 위해 작동하지 않습니다 .out + err.log가 비어 있습니다. 그리고 네, 같은 파일에서 표준 오류와 표준을 원합니다
Andreas

7

bash 스크립트 맨 위에 다음 줄을 배치 하여이 작업을 수행 할 수있었습니다.

exec 1>>log 2> >(tee -a log >&2)

이것은 stdout을 파일 log( 1>>log)으로 리디렉션 한 다음 tee stderr를 파일 log( 2> >(tee -a log)으로 리디렉션하고 다시 stderr ()로 >&2)보냅니다.이 방법으로 logstdout과 stderr을 순서대로 보여주는 단일 파일을 얻습니다 .stderr도 평소와 같이 화면에 표시됩니다.

catch는 파일에 추가 할 때만 작동하는 것 같습니다. 추가하지 않으면 두 리디렉션이 서로를 방해하는 것으로 보이며 마지막 출력 중 하나만 얻습니다.


STDERR 행을 "수정"하여 사용자 정의 문자열을 포함시켜 쉽게 잡을 수있는 방법은 무엇입니까?
IMTheNachoMan

1

콘솔과 로그 파일 모두에 표시되도록 오류 스트림을 복제하려고합니다. 이를위한 도구 tee는 오류 스트림에 적용하기 만하면됩니다. 불행히도, 명령의 오류 스트림을 다른 명령으로 파이프하기위한 표준 쉘 구성이 없으므로 약간의 파일 디스크립터 재배치가 필요합니다.

{ { echo out; echo err 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^   ^^^^^^^^^^^^    ^^^^^^^^^
  command produces output      stdout3                    log
  command produces error       stderr1   dup to terminal  log

이것은 또한 저에게 효과적이지 않습니다. 나는 stdout 줄을 먼저 얻은 다음 'log'파일의 모든 stderr 줄을 얻습니다. 예 : {{echo out; echo err 1> & 2; echo out2; 에코 err2 1> & 2; } 2> & 1> & 3 | 티 / dev / tty; }> log 3> & 1
Andreas

1
@Andreas : 아, tee여기 지연도 소개되어 있습니다. 순수한 쉘 솔루션이 있다고 생각하지 않습니다. 실제로 나는 어떤 식 으로든 응용 프로그램을 수정하지 않고 솔루션이 있는지 궁금합니다.
Gilles 'SO- 악의를 멈춰라'

1

stderr을 화면에 쓰고 BOTH stderr와 stdout을 파일에 쓰려면-stderr과 stdout의 행이 모두 화면에 쓰여졌을 때와 동일한 순서로 나오도록하려면 :

어려운 문제로 밝혀졌습니다. 특히 화면에 단순히 썼다면 "동일한 시퀀스"를 갖는 부분이 있습니다. 간단히 말해서 : 각 파일을 자체 파일에 작성하고, 배경 처리 마법을 수행하여 각 줄 (각 ​​파일에서)을 정확한 시간으로 표시 한 다음 stderr 파일을 화면에 "tail --follow"하십시오. "stderr"과 "stdout"을 순서대로 함께 보려면 두 파일 (각 줄에 정확한 시간 표시가있는)을 함께 정렬하십시오.

암호:

# Set the location of output and the "first name" of the log file(s)
pth=$HOME
ffn=my_log_filename_with_no_extension
date >>$pth/$ffn.out
date >>$pth/$ffn.err
# Start background processes to handle 2 files, by rewriting each one line-by-line as each line is added, putting a label at front of line
tail -f $pth/$ffn.out | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|1|".$_' >>$pth/$ffn.out.txt &
tail -f $pth/$ffn.err | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|2|".$_' >>$pth/$ffn.err.txt &
sleep 1
# Remember the process id of each of 2 background processes
export idout=`ps -ef | grep "tail -f $pth/$ffn.out" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
export iderr=`ps -ef | grep "tail -f $pth/$ffn.err" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
# Run the command, sending stdout to one file, and stderr to a 2nd file
bash mycommand.sh 1>>$pth/$ffn.out 2>>$pth/$ffn.err
# Remember the exit code of the command
myexit=$?
# Kill the two background processes
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"idout"}/'
echo kill $idout
kill $idout
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"iderr"}/'
echo kill $iderr
kill $iderr
date
echo "Exit code: $myexit for '$listname', list item# '$ix', bookcode '$bookcode'"

예, 이것은 정교 해 보이며 4 개의 출력 파일을 생성합니다 (이 중 2 개는 삭제할 수 있음). 해결하기 어려운 문제인 것 같으므로 몇 가지 메커니즘이 필요했습니다.

결국 예상대로 순서대로 stdout 및 stderr의 결과를 보려면 다음을 실행하십시오.

cat $pth/$ffn.out.txt $pth/$ffn.err.txt | sort

시퀀스가 stdout과 stderr 둘 다 단순히 화면에 갔을 때의 결과와 적어도 매우 가까운 유일한 이유는 다음과 같습니다. 모든 단일 행에는 밀리 초까지의 타임 스탬프가 표시됩니다.

프로세스가 진행되는 동안 화면에서 stderr를 보려면 다음을 사용하십시오.

tail -f $pth/$ffn.out

원래 질문이 나온 후 오랫동안 여기에 도착한 누군가에게 도움이되기를 바랍니다.


순전히 노력 +1, 비슷한 아이디어 (펄스를 통해 두 스트림 타임 스탬프)를 가지고 있지만 그것을 구현하기 위해 고심하고있었습니다. 그것이 나에게 효과가 있는지 조사 할 것입니다 (즉, 실제로 출력 순서를 유지한다면 다른 솔루션이 stderr와 stdout의 태깅 사이의 비 동시성으로 인해 일을 조금 이동함에 따라)
Olivier Dulac

0

하자 f다음이, 당신이 실행 좋아하려는 명령 수

( exec 3>/tmp/log; f 2>&1 1>&3 |tee >(cat)>&3 )

당신이 원하는 것을 주어야합니다. 예를 들면 wget -O - www.google.de다음과 같습니다.

( exec 3>/tmp/googlelog; wget -O - www.google.de 2>&1 1>&3 |tee >(cat)>&3 )

1
이것은 거의 나를 위해 작동하지만 로그 파일에서 먼저 모든 stdout 라인을 가져온 다음 모든 stderror 라인을 얻습니다. 나는 그들이 일어날 때 자연 순서대로 인터리브되기를 원합니다.
Andreas

0

내가 읽은 것을 감안할 때, 내가 볼 수있는 유일한 해결책은 타임 라인 / 타임 스탬프와 함께 STOUT이든 STERR이든 각 줄 앞에 추가하는 것입니다. 날짜 + % s는 몇 초가 걸리지 만 순서를 유지하기 위해 속도가 느려집니다. 또한 로그는 어떻게 든 정렬되어야합니다. 내가 할 수있는 것보다 더 많은 일.


0

나는이 정확한 요구 사항으로 어려움을 겪었고 결국 간단한 해결책을 찾을 수 없었습니다. 내가 대신 한 것은 이것입니다.

TMPFILE=/tmp/$$.log
myCommand > $TMPFILE 2>&1
[ $? != 0 ] && cat $TMPFILE
date >> myCommand.log
cat $TMPFILE >> myCommand.log
rm -f $TMPFILE

적절한 인터리브 순서로 stdout 및 stderr을 로그 파일에 추가합니다. 그리고 오류가 발생하면 (명령의 종료 상태에 따라 결정됨) 전체 출력 (stdout stderr)을 다시 적절한 인터리브 순서로 stdout으로 보냅니다 . 나는 이것이 나의 필요에 대한 합리적인 타협 인 것을 알았다. 증가하는 다중 실행 파일이 아닌 단일 실행 로그 파일을 원하는 경우 훨씬 간단합니다.

myCommand > myCommand.log 2>&1
[ $? != 0 ] && cat myCommand.log

0

stderr을 명령으로 대체 tee -a하고 stdout을 동일한 파일에 추가합니다.

./script.sh 2> >(tee -a outputfile) >>outputfile

참고 : 너무 정확한 순서를 확인하십시오 (그러나 stderr-show가없는 경우) 'expect'도구의 '언 버퍼'명령이 있습니다.

unbuffer ./script.sh > outputfile
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.