STDOUT와 STDERR를 모두 단말기와 로그파일에 보내려면 어떻게 해야 하나요?
비기술 사용자가 인터랙티브하게 실행하는 스크립트가 있습니다.스크립트는 사용자가 스크립트가 정상적으로 실행되고 있는지 확인할 수 있도록 상태 업데이트를 STDOUT에 기록합니다.
STDOUT 와 STDERR 를 모두 단말기로 리다이렉트 합니다(사용자가 스크립트가 동작하고 있는 것을 확인할 수 있을 뿐만 아니라 문제가 있는지 확인할 수 있도록 합니다).두 스트림 모두 로그 파일로 리디렉션해야 합니다.
인터넷에서 많은 해결책을 봤어요.어떤 것들은 효과가 없고 어떤 것들은 끔찍하게 복잡하다.실행 가능한 솔루션(답변으로 입력)을 개발했습니다만, 매우 애매합니다.
최적의 솔루션은 단말기와 로그파일 양쪽에 스트림을 송신하는 스크립트의 선두에 1줄의 코드를 짜넣는 것입니다.
EDIT: STDERR를 STDOUT로 리다이렉트하고 결과를 TE로 파이핑하는 것은 동작하지만, 출력의 리다이렉트 및 파이핑에 대해 기억하는 사용자에 따라 달라집니다.로그가 완전하고 자동으로 이루어지도록 하고 싶다(그 때문에 스크립트 자체에 솔루션을 삽입할 수 있도록 하고 싶다.
파일 및 화면으로 리디렉션하려면 "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
5년 후가 다가오는데...
나는 이것이 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
(주의: 이것은 Bash에서만 작동합니다./bin/sh에서는 동작하지 않습니다.)
여기서부터 각색되어 있습니다.원본은 로그 파일에서 STDERR을 포착하지 못했습니다.여기 메모로 고쳐놨어
패턴
the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )
이것에 의해, stdout 와 stderr 의 양쪽 모두가 개별적으로 리다이렉트 되어, stdout 와 stderr 의 개별 카피가 발신자(단말기일 가능성이 있습니다)에게 송신됩니다.
zsh가 될 .
tee
을 사용하다bash에서는 다음에 나오는 문 뒤에 출력의 마지막 몇 행이 표시될 수 있습니다.
어느 경우든 올바른 비트는 올바른 위치로 이동합니다.
설명.
다음은 스크립트(./예제에 저장)입니다.
#! /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이 파일 기술자에 할당됩니다.프로세스 치환으로 둘러싸여 있기 때문에 이러한 파일 기술자에 대한 경로는 calling 명령어로 대체되므로 다음과 같이 표시됩니다.
the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
를 실행하여 첫 파일 두 파일 기술자에 .stdout은 stderr을 실행합니다.1회 " " " 입니다.
the_cmd
가 종료하면, 곧바로 다음의 스테이트먼트가 발생합니다(사용의 단말기가 발신자일 경우, 프롬프트가 표시됩니다).1회 zsh 우 1 1 1 회
the_cmd
은 양쪽 합니다.tee
다음 단계로 넘어가기 전에 완료해야 합니다.자세한 것은 이쪽.번째 ★★★★★★★★★★★★★★.
tee
로부터 )the_cmd
stdout의 "stdout", "stdout", "stdout", "stdout", "stdout"이기 때문입니다.tee
않기 에, .번째 두 the the
tee
에는 ★★★★★★★★★★★★★★★★★★★★★」stdout
.stderr
요, 이 (읽다)에서 . 스틴 스틴 스틴 스틴스the_cmd
"") 에 쓸 때, 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만 필터링(예를 들어 tee)하려면 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(다른 셸과 달리)에서는 이미 열려 있는 파일 기술자를 리다이렉트하면 대신 내장된 tee와 같은 동작을 하는 경우가 있습니다(어떻게 결정하는지 완전히 이해할 수 없습니다).이를 방지하려면 리디렉션하기 전에 각 fd를 닫아야 합니다.
에 대해서 두 은 'zsh로 하다', 'zsh로 하다', '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의 불가사의한 내장 암묵 티잉을 이용하여 tee를 전혀 실행하지 않는 zsh를 위한 훨씬 짧은 솔루션을 얻을 수 있습니다.
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
도 못 >&1
★★★★★★★★★★★★★★★★★」2>&2
티잉을 트리거하는 를 통해 알 수 .)zsh는 그것을 알 수 있었습니다.
Tee 프로그램과 dup stderr를 사용하여 stdout을 수행합니다.
program 2>&1 | tee > logfile
하다를 사용하세요.script
(남자 1인)
script()를 설정하고 exit를 호출하는 래퍼 셸스크립트(2줄)를 만듭니다.
파트 1: 랩쉿
#!/bin/sh
script -c './realscript.sh'
exit
파트 2: 실사본쉿
#!/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
~:
저는 'RunScript.sh'이라는 대본을 만들었습니다.이 스크립트의 내용은 다음과 같습니다.
${APP_HOME}/${1}.sh ${2} ${3} ${4} ${5} ${6} 2>&1 | tee -a ${APP_HOME}/${1}.log
저는 이렇게 부릅니다.
./RunScript.sh ScriptToRun Param1 Param2 Param3 ...
이 방법은 작동하지만 외부 스크립트를 통해 응용 프로그램의 스크립트를 실행해야 합니다.좀 촌스럽네요.
1년 후, 여기 기록하기 위한 오래된 bash 스크립트가 있습니다.예를들면,
teelog make ...
생성된 로그 이름에 로그(및 네스트된 로깅 트릭 참조)make
s도 마찬가지입니다.)
#!/bin/bash
me=teelog
Version="2008-10-9 oct denis-bz"
Help() {
cat <<!
$me anycommand args ...
logs the output of "anycommand ..." as well as displaying it on the screen,
by running
anycommand args ... 2>&1 | tee `day`-command-args.log
That is, stdout and stderr go to both the screen, and to a log file.
(The Unix "tee" command is named after "T" pipe fittings, 1 in -> 2 out;
see http://en.wikipedia.org/wiki/Tee_(command) ).
The default log file name is made up from "command" and all the "args":
$me cmd -opt dir/file logs to `day`-cmd--opt-file.log .
To log to xx.log instead, either export log=xx.log or
$me log=xx.log cmd ...
If "logdir" is set, logs are put in that directory, which must exist.
An old xx.log is moved to /tmp/\$USER-xx.log .
The log file has a header like
# from: command args ...
# run: date pwd etc.
to show what was run; see "From" in this file.
Called as "Log" (ln -s $me Log), Log anycommand ... logs to a file:
command args ... > `day`-command-args.log
and tees stderr to both the log file and the terminal -- bash only.
Some commands that prompt for input from the console, such as a password,
don't prompt if they "| tee"; you can only type ahead, carefully.
To log all "make" s, including nested ones like
cd dir1; \$(MAKE)
cd dir2; \$(MAKE)
...
export MAKE="$me make"
!
# See also: output logging in screen(1).
exit 1
}
#-------------------------------------------------------------------------------
# bzutil.sh denisbz may2008 --
day() { # 30mar, 3mar
/bin/date +%e%h | tr '[A-Z]' '[a-z]' | tr -d ' '
}
edate() { # 19 May 2008 15:56
echo `/bin/date "+%e %h %Y %H:%M"`
}
From() { # header # from: $* # run: date pwd ...
case `uname` in Darwin )
mac=" mac `sw_vers -productVersion`"
esac
cut -c -200 <<!
${comment-#} from: $@
${comment-#} run: `edate` in $PWD `uname -n` $mac `arch`
!
# mac $PWD is pwd -L not -P real
}
# log name: day-args*.log, change this if you like --
logfilename() {
log=`day`
[[ $1 == "sudo" ]] && shift
for arg
do
log="$log-${arg##*/}" # basename
(( ${#log} >= 100 )) && break # max len 100
done
# no blanks etc in logfilename please, tr them to "-"
echo $logdir/` echo "$log".log | tr -C '.:+=[:alnum:]_\n' - `
}
#-------------------------------------------------------------------------------
case "$1" in
-v* | --v* )
echo "$0 version: $Version"
exit 1 ;;
"" | -* )
Help
esac
# scan log= etc --
while [[ $1 == [a-zA-Z_]*=* ]]; do
export "$1"
shift
done
: ${logdir=.}
[[ -w $logdir ]] || {
echo >&2 "error: $me: can't write in logdir $logdir"
exit 1
}
: ${log=` logfilename "$@" `}
[[ -f $log ]] &&
/bin/mv "$log" "/tmp/$USER-${log##*/}"
case ${0##*/} in # basename
log | Log ) # both to log, stderr to caller's stderr too --
{
From "$@"
"$@"
} > $log 2> >(tee /dev/stderr) # bash only
# see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe
;;
* )
#-------------------------------------------------------------------------------
{
From "$@" # header: from ... date pwd etc.
"$@" 2>&1 # run the cmd with stderr and stdout both to the log
} | tee $log
# mac tee buffers stdout ?
esac
이 문제는 아직 원만하게 해결되지 않은 것 같다."stdout과 stderr에 동시에 출력하는 방법"을 검색할 때마다 구글에서 이 게시물로 안내합니다.
오늘, 저는 마침내 이러한 종류의 요구를 거의 모두 해결할 수 있는 간단하고 효과적인 방법을 찾았습니다.
중요한 아이디어는 동시에 여러 출력에 인쇄할 수 있는 tee 명령과 Linux 고유의 /proc/self/fd/{1,2,...}은(는) stdout을 나타냅니다. stderr...
- stdin을 stdout과 stderr 양쪽에 인쇄합니다.
tee /proc/self/fd/2
- stdin을 stdout과 stderr 양쪽에 인쇄하고 파일을 만듭니다.
tee /proc/self/fd/2 file
도움이 되길 바랍니다.
다음은 "kvantour, MatrixManAtyrService"와 "Jason Sydes"의 솔루션을 결합하여 리다이렉션을 통한 bash를 위한 솔루션입니다.
#!/bin/bash
exec 1> >(tee x.log) 2> >(tee x.err >&2)
echo "test for log"
echo "test for err" 1>&2
위의 스크립트를 x.sh 로 저장합니다../x.sh 실행 후 x.log에는 stdout만 포함되며 x.err에는 stderr만 포함됩니다.
언급URL : https://stackoverflow.com/questions/363223/how-do-i-get-both-stdout-and-stderr-to-go-to-the-terminal-and-a-log-file
'bestsource' 카테고리의 다른 글
PowerShell에서 경로를 정규화하는 방법 (0) | 2023.04.19 |
---|---|
CMD 쉘의 사용 가능한 공간 (0) | 2023.04.19 |
WPF에서의 탭 순서 설정 (0) | 2023.04.19 |
WPF: TextBox에서 포커스를 프로그래밍 방식으로 삭제하는 방법 (0) | 2023.04.19 |
다른 페이지에서 로그인이 필요한 페이지에 액세스하기 위한 CURL (0) | 2023.04.19 |