STDOUT과 STDERR 모두 터미널과 로그 파일로 이동하려면 어떻게해야합니까?
비 기술적 인 사용자가 대화식으로 실행할 스크립트가 있습니다. 스크립트는 사용자가 스크립트가 정상적으로 실행되고 있는지 확인할 수 있도록 상태 업데이트를 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
(참고 : 이것은 Bash에서만 작동합니다. / bin / sh 에서는 작동 하지 않습니다 .)
여기 에서 적응 ; 원본은 내가 알 수 있듯이 로그 파일에서 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
tee 프로그램을 사용하고 stderr을 stdout으로 복사하십시오.
program 2>&1 | tee > logfile
"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
).
#!/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 간의 구별을 유지합니다.
{ the_cmd > >(tee stdout.txt ); } 2> >(tee stderr.txt >&2 )
다음은 스크립트입니다.
the_cmd()
{
echo out;
1>&2 echo err;
}
{ the_cmd > >(tee stdout.txt ); } 2> >(tee stderr.txt >&2 )
세션은 다음과 같습니다.
$ foo=$(./example.sh)
err
$ echo $foo
out
$ cat stdout.txt
out
$ cat stderr.txt
err
작동 방식은 다음과 같습니다.
stdout of
the_cmd
is sent totee
, which saves it to a file, but also passes a copy along to stdout of its block (the part in {braces}). stderr ofthe_cmd
makes it to the block's stderr by default.We don't redirect stdout from the block (since we've already captured it in a file) so it makes it to the terminal by default, but we do redirect the block's stderr to another tee command (since we still need to capture it).
The final
tee
command writes its stdin (which isthe_cmd
's stderr) to a file, but also passes a copy to stdout, which would mix it with the block's stdout (we don't want that), so we explicitly send it back to stderr with>&2
.
This keeps stderr separate from stdout both in the files and in the command's output.
If the first tee writes any errors, they'll show up in both the stderr file and in the command's stderr, if the seconds tee writes any errors, they'll only show up only in the terminal's stderr.
Use the script
command in your script (man 1 script)
Create a wrapper shellscript (2 lines) that sets up script() and then calls exit.
Part 1: wrap.sh
#!/bin/sh
script -c './realscript.sh'
exit
Part 2: realscript.sh
#!/bin/sh
echo 'Output'
Result:
~: 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
~:
'Programing' 카테고리의 다른 글
IntelliJ에서 빌더 패턴 코드 생성 (0) | 2020.10.19 |
---|---|
Entity Framework : 특정 쿼리에 대해 지연로드를 비활성화하는 방법은 무엇입니까? (0) | 2020.10.19 |
jQuery에서 JSON 배열을 HTML 테이블로 변환 (0) | 2020.10.19 |
.gitignore에서 부정 패턴은 어떻게 작동합니까? (0) | 2020.10.19 |
Oracle SQL에서 작은 따옴표를 처리하는 방법 (0) | 2020.10.19 |