비 기술적 인 사용자가 대화식으로 실행할 스크립트가 있습니다. 스크립트는 사용자가 스크립트가 정상적으로 실행되고 있는지 확인할 수 있도록 상태 업데이트를 STDOUT에 기록합니다.
STDOUT과 STDERR 모두 터미널로 리디렉션되기를 원합니다 (사용자가 스크립트가 작동하고 문제가 있는지 확인할 수 있도록). 또한 두 스트림 모두 로그 파일로 리디렉션되기를 원합니다.
인터넷에서 많은 솔루션을 보았습니다. 일부는 작동하지 않고 다른 일부는 끔찍하게 복잡합니다. 나는 실행 가능한 솔루션을 개발했지만 (답으로 입력 할 것입니다), 어리 석습니다.
완벽한 솔루션은 터미널과 로그 파일 모두에 두 스트림을 모두 보내는 스크립트의 시작 부분에 통합 될 수있는 코드 한 줄입니다.
편집 : STDERR을 STDOUT으로 리디렉션하고 결과를 티에 파이프 작업하지만 출력을 리디렉션하고 파이프하는 것을 기억하는 사용자에 따라 다릅니다. 로깅이 안전하고 자동으로 이루어지기를 원합니다 (그래서 스크립트 자체에 솔루션을 포함 할 수 있기를 원합니다.)
답변
“tee”를 사용하여 파일과 화면으로 리디렉션합니다. 사용하는 셸에 따라 먼저 다음을 사용하여 stderr을 stdout으로 리디렉션해야합니다.
./a.out 2>&1 | tee output
또는
./a.out |& tee output
csh에는 화면으로 이동하는 모든 것을 파일로 캡처하는 “script”라는 내장 명령이 있습니다. “script”를 입력하여 시작하고 캡처하려는 작업을 수행 한 다음 control-D를 눌러 스크립트 파일을 닫습니다. sh / bash / ksh에 해당하는 항목을 모르겠습니다.
또한 수정할 수있는 자체 sh 스크립트라고 표시 했으므로 전체 스크립트를 다음과 같이 중괄호 또는 대괄호로 묶어 내부적으로 리디렉션을 수행 할 수 있습니다.
#!/bin/sh
{
... whatever you had in your script before
} 2>&1 | tee output.file
답변
50 년이 지난 후 …
나는 이것이 OP가 추구하는 “완벽한 해결책”이라고 믿는다.
다음은 Bash 스크립트 상단에 추가 할 수있는 한 줄입니다.
exec > >(tee -a $HOME/logfile) 2>&1
다음은 그 사용을 보여주는 작은 스크립트입니다.
#!/usr/bin/env bash
exec > >(tee -a $HOME/logfile) 2>&1
# Test redirection of STDOUT
echo test_stdout
# Test redirection of STDERR
ls test_stderr___this_file_does_not_exist
(참고 : 이것은 단지 강타와 함께 작동 그것은 것이다. 하지 / bin / sh 작동 .)
여기 에서 적응 ; 원본은 내가 알 수 있듯이 로그 파일에서 STDERR을 포착하지 못했습니다. 여기 에서 메모로 수정 되었습니다 .
답변
패턴
the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )
이 표준 에러 별도로 표준 출력 모두를 리디렉션, 그리고 전송 분리 (터미널이 될 수 있음) 호출자에게 표준 출력 및 표준 오류의 사본을.
-
zsh에서는
tee
s가 완료 될 때까지 다음 문으로 진행되지 않습니다 . -
배쉬에서는 출력의 마지막 몇 줄이 나타나지 않을 수 있습니다 후 다음에 오는 어떤 문.
두 경우 모두 올바른 비트가 올바른 위치로 이동합니다.
설명
다음은 스크립트 (./example에 저장 됨)입니다.
#! /usr/bin/env bash
the_cmd()
{
echo out;
1>&2 echo err;
}
the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )
세션은 다음과 같습니다.
$ foo=$(./example)
err
$ echo $foo
out
$ cat stdout.txt
out
$ cat stderr.txt
err
작동 방식은 다음과 같습니다.
- 두
tee
프로세스가 모두 시작되고 해당 stdin이 파일 설명자에 할당됩니다. 프로세스 대체로 묶여 있기 때문에 해당 파일 설명자에 대한 경로는 호출 명령에서 대체되므로 이제 다음과 같이 보입니다.
the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
-
the_cmd
실행되고 stdout을 첫 번째 파일 설명자에 쓰고 stderr을 두 번째 파일 설명자에 씁니다. -
bash의 경우
the_cmd
완료되면 다음 명령문이 즉시 발생합니다 (터미널이 호출자 인 경우 프롬프트가 표시됨). -
zsh의 경우
the_cmd
완료되면 셸은tee
계속 진행하기 전에 두 프로세스가 모두 완료 될 때까지 기다립니다 . 여기 에 대한 자세한 내용 . -
의 stdout
tee
에서 읽는 첫 번째 프로세스the_cmd
는 해당 stdout의 복사본을 호출자에게 다시 씁니다tee
. 출력은 리디렉션되지 않으므로 변경되지 않은 상태로 호출자에게 다시 보냅니다. -
두 번째
tee
프로세스는stdout
호출자에게 리디렉션됩니다stderr
(stdin이the_cmd
‘s stderr 에서 읽고 있기 때문에 좋습니다 ). 따라서 stdout에 쓸 때 해당 비트는 호출자의 stderr로 이동합니다.
이렇게하면 파일과 명령 출력 모두에서 stderr을 stdout과 별도로 유지합니다.
첫 번째 티가 오류를 작성하면 stderr 파일과 명령의 stderr에 모두 표시되고 두 번째 티가 오류를 작성하면 터미널의 stderr에만 표시됩니다.
답변
stderr을 stdout으로 리디렉션하려면 명령에 다음을 추가하십시오. 2>&1
터미널로 출력하고 파일에 로그인하려면 다음을 사용해야합니다.tee
둘 다 함께 다음과 같이 보일 것입니다.
mycommand 2>&1 | tee mylogfile.log
편집 : 스크립트에 포함하려면 똑같이 할 것입니다. 그래서 당신의 스크립트
#!/bin/sh
whatever1
whatever2
...
whatever3
결국
#!/bin/sh
( whatever1
whatever2
...
whatever3 ) 2>&1 | tee mylogfile.log
답변
편집 : 탈선하여 요청한 질문과 다른 질문에 답하게 된 것을 봅니다. 실제 질문에 대한 답변은 Paul Tomblin의 답변 하단에 있습니다. (어떤 이유로 stdout과 stderr를 별도로 리디렉션하도록 해당 솔루션을 향상 시키려면 여기에서 설명하는 기술을 사용할 수 있습니다.)
나는 stdout과 stderr의 구별을 유지하는 답변을 원했습니다. 불행히도 지금까지 그 구별을 유지하는 모든 답변은 인종에 취약합니다. 제가 코멘트에서 지적했듯이 프로그램이 불완전한 입력을 보는 위험이 있습니다.
나는 마침내 구별을 유지하고 인종에 취약하지 않고 끔찍하게 어리석지 않은 답을 찾았다 고 생각합니다.
첫 번째 빌딩 블록 : stdout과 stderr를 바꾸려면 :
my_command 3>&1 1>&2 2>&3-
두 번째 빌딩 블록 : stderr 만 필터링 (예 : 티)하려면 stdout & stderr를 교체하고 필터링 한 다음 다시 교체하여이를 수행 할 수 있습니다.
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
이제 나머지는 간단합니다. 처음에 stdout 필터를 추가 할 수 있습니다.
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
또는 끝에 :
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
위의 두 명령이 모두 작동한다는 것을 확신하기 위해 다음을 사용했습니다.
alias my_command='{ echo "to stdout"; echo "to stderr" >&2;}'
alias stdout_filter='{ sleep 1; sed -u "s/^/teed stdout: /" | tee stdout.txt;}'
alias stderr_filter='{ sleep 2; sed -u "s/^/teed stderr: /" | tee stderr.txt;}'
출력은 다음과 같습니다.
...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr
내 프롬프트는 teed stderr: to stderr
예상대로 ” ” 바로 뒤에 다시 표시 됩니다.
zsh에 대한 각주 :
위의 솔루션은 bash에서 작동하지만 (확실하지 않은 다른 셸도있을 수 있음) zsh에서는 작동하지 않습니다. zsh에서 실패하는 이유는 두 가지입니다.
- 구문
2>&3-
은 zsh에서 이해되지 않습니다. 다시 작성해야합니다.2>&3 3>&-
- zsh에서 (다른 셸과 달리) 이미 열려있는 파일 설명자를 리디렉션하면 경우에 따라 (결정 방법을 완전히 이해하지 못함) 대신 내장 티와 같은 동작을 수행합니다. 이를 방지하려면 리디렉션하기 전에 각 fd를 닫아야합니다.
예를 들어 두 번째 솔루션은 zsh에 대해 {my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(bash에서도 작동하지만 매우 장황합니다) 로 다시 작성해야합니다 .
반면에 zsh의 신비한 내장 암시 적 티잉을 활용하여 티를 전혀 실행하지 않는 zsh에 대한 훨씬 더 짧은 솔루션을 얻을 수 있습니다.
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
(필자는 문서에서 짐작하지 않았을 나는 것을 발견 >&1
하고 2>&2
트리거 zsh을의 암시 티잉하는 것입니다,. 내가 시행 착오에 의한 것을 발견)
답변
사용 script
스크립트 명령을 (man 1 스크립트).
script ()를 설정하고 exit를 호출하는 래퍼 셸 스크립트 (2 줄)를 만듭니다.
1 부 : wrap.sh
#!/bin/sh
script -c './realscript.sh'
exit
2 부 : realscript.sh
#!/bin/sh
echo 'Output'
결과:
~: sh wrap.sh
Script started, file is typescript
Output
Script done, file is typescript
~: cat typescript
Script started on fr. 12. des. 2008 kl. 18.07 +0100
Output
Script done on fr. 12. des. 2008 kl. 18.07 +0100
~:
답변
tee 프로그램을 사용하고 stderr을 stdout으로 복제하십시오.
program 2>&1 | tee > logfile
