Programing

STDOUT과 STDERR 모두 터미널과 로그 파일로 이동하려면 어떻게해야합니까?

lottogame 2020. 10. 19. 07:37
반응형

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

작동 방식은 다음과 같습니다.

  1. stdout of the_cmd is sent to tee, which saves it to a file, but also passes a copy along to stdout of its block (the part in {braces}). stderr of the_cmd makes it to the block's stderr by default.

  2. 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).

  3. The final tee command writes its stdin (which is the_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
~:

참고URL : https://stackoverflow.com/questions/363223/how-do-i-get-both-stdout-and-stderr-to-go-to-the-terminal-and-a-log-file

반응형