exec를 사용하여 모든 후속 명령의 stderr을 리디렉션합니다.

exec를 사용하여 모든 후속 명령의 stderr을 리디렉션합니다.

모든 출력을 파일, 디버그 로그 및 터미널로 리디렉션해야 하는 bash 파일이 있습니다. 스크립트의 모든 명령을 디버깅하고 기록하려면 stdout 및 stderr을 리디렉션해야 합니다.

2>&1 | tee -a $DEBUG파일의 모든 명령에 대해 추가하고 싶지 않습니다 . 나는 와 함께 살 수 있다 | tee -a $DEBUG.

비슷한 것으로 이것을 할 수 있는 방법이 있었던 것으로 기억합니다 exec 2>&1.

현재 나는 다음과 같은 것을 사용하고 있습니다.

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

하지만 작동하지 않습니다. 누구든지 해결책이 있거나 이유를 설명할 수 있습니까?

답변1

다음과 같이 스크립트 상단에서 exec를 사용할 수 있습니다.

exec > >(tee "$HOME/somefile.log") 2>&1

예를 들어:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

다음과 같이 파일 $HOME/somefile.log과 터미널로 출력하겠습니다.

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye

답변2

한 번에 많은 수의 명령을 리디렉션하는 솔루션은 다음과 같습니다.

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

원래 솔루션이 작동하지 않는 이유: exec 2>&1은 표준 오류 출력을 콘솔에서 스크립트를 실행하는 경우 콘솔이 되는 쉘의 표준 출력으로 리디렉션합니다. 명령에 대한 파이프 리디렉션은 명령의 표준 출력만 리디렉션합니다.

관점에서 보면 somecommand, 표준 출력은 연결된 파이프로 들어가고 tee, 표준 오류는 쉘의 표준 오류와 동일한 파일/의사 파일로 들어가며, 쉘의 표준 출력으로 리디렉션됩니다. 플랫폼에서 프로그램 실행에서 제어할 수 있습니다.

이를 설명하는 한 가지 실제 방법은 실제로 무슨 일이 일어나고 있는지 살펴보는 것입니다.

터미널에서 셸을 실행하면 원래 환경은 다음과 같을 수 있습니다.

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

표준 오류를 표준 출력( exec 2>&1)으로 리디렉션한 후 기본적으로 아무것도 변경하지 않습니다. 그러나 스크립트의 표준 출력을 파일로 리디렉션하면 다음과 같은 환경이 됩니다.

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

그러면 쉘 표준 오류를 표준 출력으로 리디렉션하면 다음과 같이 됩니다.

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

명령을 실행하면 이 환경이 상속됩니다. 명령을 실행하고 이를 tee로 파이프하면 명령 환경은 다음과 같습니다.

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

따라서 명령의 표준 오류는 여전히 표준 오류로 쉘에 전달됩니다.

/proc/[pid]/fd:use를 보면 실제로 명령 환경을 볼 수 ls -l있으며 심볼릭 링크의 내용도 나열할 수 있습니다. 여기에 있는 파일은 0표준 입력, 1표준 출력 2및 표준 오류입니다. 명령이 더 많은 파일을 열면(대부분의 프로그램이 수행함) 해당 파일도 볼 수 있습니다. 프로그램은 표준 입력/출력 을 리디렉션하거나 닫고 이를 재사용하도록 선택할 수도 있습니다 0.12

답변3

stderr 및 stdout을 파일에 쓰고 stderr를 화면에 표시합니다(stdout에서).

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

cron에 유용하므로 메일을 통해 오류(및 오류만)를 받을 수 있습니다.

답변4

매뉴얼 script페이지에는 몇 가지 경고가 있습니다.

비대화형 셸에서 스크립트를 실행하는 것은 권장되지 않습니다. 스크립트의 내부 셸은 항상 대화형이므로 예기치 않은 결과가 발생할 수 있습니다.

또한 스크립트는 예상보다 더 많은 입력을 읽을 수 있으므로 명령 파이프라인에서 스크립트를 사용하지 않아야 합니다.

script표준 입력 및/또는 표준 출력이 리디렉션될 때 사용하는 것은 안전하지 않다고 생각합니다.

명령을 실행할 때 다음을 수행합니다.

output=$("<command> <args>" 2> tee -a $LOGFILE >&2)

OP는 모든 명령에 대해 정확히 동일한 작업을 수행하기를 원하지 않지만 함수를 쉽게 정의할 수 있다는 것을 이해합니다.

run () { ... output=$("$(@) 2> tee -a $LOGFILE >&2"; ... }

이를 통해 일반 출력을 캡처하고 처리하는 동시에 경고 및 오류를 기록하고 동시에 stderr에 표시할 수 있습니다.

그럼 당신은 할 수 있습니다

[ $? -eq 0 ] && echo "<command> <args> succeeded! Output:\n$output\n(EOF)" >>$LOGFILE

원하는 경우 일반 출력도 기록할 수 있습니다.

관련 정보