bash 스크립트 자체에서 stdout COPY를 로그 파일로 리디렉션


235

stdout 을 파일 로 리디렉션 하는 방법을 알고 있습니다.

exec > foo.log
echo test

이것은 'test'를 foo.log 파일에 넣습니다.

이제 출력을 로그 파일로 리디렉션하고 stdout에 유지하려고합니다.

즉, 스크립트 외부에서 사소하게 수행 할 수 있습니다.

script | tee foo.log

하지만 스크립트 자체에서 선언하고 싶습니다.

나는 시도했다

exec | tee foo.log

그러나 작동하지 않았습니다.


3
귀하의 질문이 잘못 표현되었습니다. 때 호출 '간부> foo.log', 스크립트의 표준 출력이 파일 foo.log. foo.log 이동하면 stdout으로 출력되므로 foo.log 및 tty로 출력을 원한다는 것을 의미한다고 생각합니다 .
William Pursell

내가하고 싶은 것은 | 'exec'에. "exec | tee foo.log", 불행히도 exec 호출에서 파이프 리디렉션을 사용할 수 없습니다
Vitaly Kushner

답변:


297
#!/usr/bin/env bash

# Redirect stdout ( > ) into a named pipe ( >() ) running "tee"
exec > >(tee -i logfile.txt)

# Without this, only stdout would be captured - i.e. your
# log file would not contain any error messages.
# SEE (and upvote) the answer by Adam Spiers, which keeps STDERR
# as a separate stream - I did not want to steal from him by simply
# adding his answer to mine.
exec 2>&1

echo "foo"
echo "bar" >&2

이것은 bash아닙니다 sh. 로 스크립트를 호출하면 sh myscript.sh의 행을 따라 오류가 발생합니다 syntax error near unexpected token '>'.

신호 트랩으로 작업하는 tee -i경우 신호가 발생하는 경우 출력 중단을 피하기 위해이 옵션을 사용할 수 있습니다 . (의견에 대해 JamesThomasMoon1979에게 감사합니다.)


파이프 또는 터미널에 ls쓰는지 여부에 따라 출력을 변경하는 도구 ( 예 : 색상 및 열화 된 출력 사용)는 위의 구성을 파이프로 출력하는 것을 의미합니다.

색상 화 / 컬럼 화를 시행하는 옵션이 있습니다 (예 :) ls -C --color=always. 이로 인해 색상 코드가 로그 파일에도 기록되어 읽기 어려워 집니다.


5
대부분의 시스템에서 Tee는 버퍼링되므로 스크립트가 완료 될 때까지 출력이 도착하지 않을 수 있습니다. 또한이 티는 하위 프로세스가 아닌 서브 쉘에서 실행되므로 대기를 사용하여 출력을 호출 프로세스에 동기화 할 수 없습니다. 당신이 원하는 것은 bogomips.org/rainbows.git/commit/…

14
@Barry : POSIXtee 는 출력을 버퍼링하지 않도록 지정합니다 . 대부분의 시스템에서 버퍼링하면 대부분의 시스템에서 손상됩니다. 그것은 tee내 솔루션이 아니라 구현 의 문제입니다 .
DevSolar

3
@Sebastian : exec매우 강력하지만 매우 관여합니다. 현재 stdout을 다른 파일 디스크립터에 "백업"한 다음 나중에 복구 할 수 있습니다. 구글 "bash exec tutorial"에는 많은 고급 것들이 있습니다.
DevSolar

2
@ AdamSpiers : Barry가 무엇인지 잘 모르겠습니다. Bash exec는 새로운 프로세스를 시작하지 않는 것으로 문서화 되었고 >(tee ...)파이프 / 프로세스 대체라는 표준 표준이며, &경로 재 지정은 물론 배경과는 아무런 관련이 없습니다 ...? :-)
DevSolar

11
에 전달 -i하는 것이 좋습니다 tee. 그렇지 않으면 신호 인터럽트 (트랩)가 기본 스크립트에서 stdout을 방해합니다. 예를 들어 a가 trap 'echo foo' EXIT있고를 누르면 ctrl+c" foo " 가 표시되지 않습니다 . 그래서에 대한 답변을 수정하겠습니다 exec &> >(tee -ia file).
JamesThomasMoon1979

173

허용 된 답변은 STDERR을 별도의 파일 설명 자로 유지하지 않습니다. 그것의 의미는

./script.sh >/dev/null

bar터미널에 출력되지 않고 로그 파일에만 출력 됩니다.

./script.sh 2>/dev/null

터미널 foobar터미널 모두에 출력됩니다 . 분명히 그것은 일반 사용자가 기대하는 행동이 아닙니다. 동일한 로그 파일에 추가되는 두 개의 별도 티 프로세스를 사용하여이 문제를 해결할 수 있습니다.

#!/bin/bash

# See (and upvote) the comment by JamesThomasMoon1979 
# explaining the use of the -i option to tee.
exec >  >(tee -ia foo.log)
exec 2> >(tee -ia foo.log >&2)

echo "foo"
echo "bar" >&2

(위의 내용은 처음에는 로그 파일을 자르지 않습니다.이 동작을 원하면 추가해야합니다.

>foo.log

스크립트 상단에.)

의 POSIX.1-2008 사양tee(1) 이 출력 버퍼링이 필요하다, 즉 심지어 라인 버퍼링 그래서 STDOUT 및 STDERR가 동일한 행에 끝낼 수있다이 경우에 foo.log; 그러나 이는 터미널에서도 발생할 수 있으므로 로그 파일은 터미널의 정확한 미러가 아닌 경우 터미널에서 볼 있는 내용을 충실히 반영 합니다. STDOUT 행을 STDERR 행과 완전히 분리하려면 각 행에 날짜 소인 접 두부가있는 두 개의 로그 파일을 사용하여 나중에 시간순으로 다시 어셈블 할 수 있습니다.


어떤 이유로, 필자의 경우 스크립트가 c-program system () 호출에서 실행될 때 주 스크립트가 종료 된 후에도 두 개의 티 하위 프로세스가 계속 존재합니다. 따라서 다음과 같은 트랩을 추가해야했습니다.exec > >(tee -a $LOG) trap "kill -9 $! 2>/dev/null" EXIT exec 2> >(tee -a $LOG >&2) trap "kill -9 $! 2>/dev/null" EXIT
alveko

15
에 전달 -i하는 것이 좋습니다 tee. 그렇지 않으면 신호 인터럽트 (트랩)가 스크립트에서 stdout을 방해합니다. 예를 들어, trap 'echo foo' EXIT을 누른 다음을 누르면 ctrl+c" foo " 가 표시되지 않습니다 . 그래서에 대한 답변을 수정하겠습니다 exec > >(tee -ia foo.log).
JamesThomasMoon1979

나는 이것을 바탕으로 약간의 "소스 가능한"스크립트를 만들었습니다. 같은 스크립트에서 사용할 수 있습니다 . log또는 . log foo.log: sam.nipl.net/sh/log sam.nipl.net/sh/log-a
샘 왓킨스

1
이 방법의 문제점은 메시지 STDOUT가 먼저 배치로 STDERR표시 되고 메시지가 표시 된다는 것입니다. 일반적으로 예상대로 인터리브되지 않습니다.
CMCDragonkai

28

busybox, macOS bash 및 비 -bash 쉘을위한 솔루션

수락 된 답변은 bash에 가장 적합한 선택입니다. bash에 액세스하지 않고 Busybox 환경에서 작업하고 있으며 exec > >(tee log.txt)구문을 이해하지 못합니다 . 또한하지 않습니다exec >$PIPE 명명 된 파이프와 동일한 이름을 가진 일반 파일을 만들려고 시도하여 제대로 작동 실패하고 중단됩니다.

바라건대 이것은 bash가없는 다른 사람에게 유용 할 것입니다.

또한 명명 된 파이프를 사용하는 사람 rm $PIPE은 VFS에서 파이프를 연결 해제하기 때문에 안전 하지만 파이프를 사용하는 프로세스는 완료 될 때까지 참조 카운트를 유지합니다.

$ *의 사용이 반드시 안전한 것은 아닙니다.

#!/bin/sh

if [ "$SELF_LOGGING" != "1" ]
then
    # The parent process will enter this branch and set up logging

    # Create a named piped for logging the child's output
    PIPE=tmp.fifo
    mkfifo $PIPE

    # Launch the child process with stdout redirected to the named pipe
    SELF_LOGGING=1 sh $0 $* >$PIPE &

    # Save PID of child process
    PID=$!

    # Launch tee in a separate process
    tee logfile <$PIPE &

    # Unlink $PIPE because the parent process no longer needs it
    rm $PIPE    

    # Wait for child process, which is running the rest of this script
    wait $PID

    # Return the error code from the child process
    exit $?
fi

# The rest of the script goes here

이것이 지금까지 맥에서 작동하는 유일한 솔루션입니다
Mike Baglio Jr.

19

스크립트 파일 안에 다음과 같이 모든 명령을 괄호 안에 넣으십시오.

(
echo start
ls -l
echo end
) | tee foo.log

5
pedantically, 또한 중괄호 ( {})를 사용할 수 있습니다
glenn jackman

글쎄, 나는 그것을 고려했지만, 이것은 현재 쉘 stdout의 리디렉션, 그 종류의 속임수, 실제로 서브 쉘을 실행하고 정기적 인 파이퍼 리디렉션을 수행합니다. 생각을 작동합니다. 나는 이것과 "tail -f foo.log &"솔루션으로 나뉩니다. 표면이 더 좋은지 조금 기다립니다. 아마 정착하지 않을 경우;)
Vitaly Kushner

8
{}는 현재 쉘 환경에서 목록을 실행합니다. ()는 서브 쉘 환경에서 목록을 실행합니다.

제길. 감사합니다. Windows 시스템에서 MingW에서 스크립트가 실행되도록 예약하려고 시도했지만 받아 들인 대답이 효과가 없었습니다. 구현되지 않은 프로세스 대체에 대해 불평했습니다. 이 답변은 다음 변경 후 stderr 및 stdout을 모두 캡처하는 데 효과적이었습니다.```-) | tee foo.log +) 2> & 1 | tee foo.log
Jon Carter

14

bash 스크립트 로그를 syslog에 만드는 쉬운 방법. 스크립트 출력은 /var/log/syslogstderr를 통해 또는 stderr을 통해 사용 가능합니다 . syslog는 타임 스탬프를 포함한 유용한 메타 데이터를 추가합니다.

이 줄을 맨 위에 추가하십시오.

exec &> >(logger -t myscript -s)

또는 별도의 파일로 로그를 보내십시오.

exec &> >(ts |tee -a /tmp/myscript.output >&2 )

이 필요 moreutils합니다 (위해 ts타임 스탬프를 추가 명령).


10

수락 된 답변을 사용하면 스크립트가 나머지 부분을 백그라운드에서 실행하면서 남겨두고 ( 'exec>> (tee ...)'직후) 예외적으로 일찍 반환했습니다. 해당 솔루션을 내 방식대로 작동시킬 수 없으므로 문제에 대한 다른 솔루션 / 해결 방법을 찾았습니다.

# Logging setup
logfile=mylogfile
mkfifo ${logfile}.pipe
tee < ${logfile}.pipe $logfile &
exec &> ${logfile}.pipe
rm ${logfile}.pipe

# Rest of my script

이렇게하면 스크립트의 출력이 프로세스에서 파이프를 통해 'tee'의 하위 백그라운드 프로세스로 이동하여 모든 것을 디스크 및 스크립트의 원래 stdout에 기록합니다.

'exec &>'는 stdout과 stderr을 모두 리디렉션하므로 원하는 경우 별도로 리디렉션하거나 stdout을 원하면 'exec>'로 변경할 수 있습니다.

스크립트가 시작될 때 파이프가 파일 시스템에서 제거 되더라도 프로세스가 완료 될 때까지 계속 작동합니다. rm 행 다음에 파일 이름을 사용하여 참조 할 수 없습니다.


David Z두 번째 아이디어 와 비슷한 답변 입니다. 그 의견을 살펴보십시오. +1 ;-)
올리버

잘 작동합니다. 의 $logfile일부를 이해하고 있지 않습니다 tee < ${logfile}.pipe $logfile &. 특히, set -x으로 변경하여 stdout에서 '+'없이 행만 표시하면서에 대한 전체 확장 명령 로그 행 ( )을 파일 로 캡처하도록 변경하려고했지만에 (tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &대한 오류 메시지가 표시되었습니다 $logfile. tee좀 더 자세히 설명해 줄 수 있습니까 ?
Chris Johnson

나는 이것을 테스트 했고이 답변이 STDERR (STDOUT과 병합 됨)을 보존하지 않는 것처럼 보이므로 오류 감지 또는 다른 리디렉션을 위해 별도의 스트림에 의존하는 경우 Adam의 답변을 봐야합니다.
HeroCC


1

exec를 기반으로 한 솔루션에 익숙하다고 말할 수는 없습니다. 나는 tee를 직접 사용하는 것을 선호하므로 요청시 tee로 스크립트를 호출합니다.

# my script: 

check_tee_output()
{
    # copy (append) stdout and stderr to log file if TEE is unset or true
    if [[ -z $TEE || "$TEE" == true ]]; then 
        echo '-------------------------------------------' >> log.txt
        echo '***' $(date) $0 $@ >> log.txt
        TEE=false $0 $@ 2>&1 | tee --append log.txt
        exit $?
    fi 
}

check_tee_output $@

rest of my script

이를 통해 다음을 수행 할 수 있습니다.

your_script.sh args           # tee 
TEE=true your_script.sh args  # tee 
TEE=false your_script.sh args # don't tee
export TEE=false
your_script.sh args           # tee

예를 들어 tee = false를 대신 기본값으로 설정하고 TEE가 대신 로그 파일을 보유하도록하십시오.이 솔루션은 jbarlow와 유사하지만 더 간단합니다.


-1

이들 중 어느 것도 완벽한 솔루션은 아니지만 다음은 시도해 볼 수있는 몇 가지 사항입니다.

exec >foo.log
tail -f foo.log &
# rest of your script

또는

PIPE=tmp.fifo
mkfifo $PIPE
exec >$PIPE
tee foo.log <$PIPE &
# rest of your script
rm $PIPE

두 번째는 스크립트에 문제가 발생하면 문제가 될 수도 있고 그렇지 않을 수도있는 파이프 파일을 그대로 두는 것 rm입니다.


1
tail은 두 번째 스크립트 티에서 차단되는 실행 프로세스를 남겨 두거나 &로 실행해야합니다.이 경우 첫 번째 스크립트와 같이 프로세스를 종료합니다.
Vitaly Kushner가

@Vitaly : 죄송합니다, 배경으로 잊어 버렸습니다 tee-편집했습니다. 내가 말했듯이 완벽한 솔루션은 아니지만 부모 셸이 종료되면 백그라운드 프로세스가 종료되므로 리소스를 영원히 낭비하는 것에 대해 걱정할 필요가 없습니다.
David Z

1
Yikes : 이것들은 매력적으로 보이지만 tail -f의 출력도 foo.log로갑니다. exec 앞에 tail -f를 실행하면 문제를 해결할 수 있지만 부모가 종료 된 후에도 꼬리는 계속 실행됩니다. 함정 0으로 명시 적으로 처치해야합니다.
William Pursell

응 스크립트가 백그라운드에 있으면 프로세스가 끝납니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.