스크립트 자체 내에서 전체 셸 스크립트의 출력을 어떻게 리디렉션합니까?


205

Bourne 쉘 스크립트의 모든 출력을 어딘가로 리디렉션 할 수 있습니까?하지만 스크립트 자체 내에 쉘 명령이 있습니까?

단일 명령의 출력을 리디렉션하는 것은 쉽지만 다음과 같은 것을 원합니다.

#!/bin/sh
if [ ! -t 0 ]; then
    # redirect all of my output to a file here
fi

# rest of script...

의미 : 스크립트가 비 대화식으로 실행되는 경우 (예 : cron) 모든 파일의 출력을 파일로 저장하십시오. 쉘에서 대화식으로 실행하는 경우 평소와 같이 출력이 stdout으로 이동하십시오.

FreeBSD 주기적 유틸리티가 일반적으로 실행하는 스크립트에 대해이 작업을 수행하려고합니다. 매일 실행되는 이메일의 일부로 매일 보지 않아도되므로 발송하지 않습니다. 그러나이 하나의 특정 스크립트 내에서 무언가 실패하면 나에게 중요하며 일상적인 작업 의이 부분의 출력을 캡처하고 이메일로 보내려고합니다.

업데이트 : Joshua의 대답은 현장에 있지만, 전체 스크립트 주위에 stdout 및 stderr을 저장하고 복원하고 싶었습니다.

# save stdout and stderr to file descriptors 3 and 4, then redirect them to "foo"
exec 3>&1 4>&2 >foo 2>&1

# ...

# restore stdout and stderr
exec 1>&3 2>&4

2
대화식 모드를 테스트하는 가장 좋은 방법은 $ TERM 테스트가 아닙니다. 대신 stdin이 tty인지 여부를 테스트하십시오 (test -t 0).
Chris Jester-Young

2
다시 말해 : if [! -t 0]; 그런 다음 exec> somefile 2> & 1; fi
Chris Jester-Young

1
모든 장점을 보려면 여기를 참조하십시오. http://tldp.org/LDP/abs/html/io-redirection.html 기본적으로 Joshua가 말한 내용입니다. exec> 파일은 stdout을 특정 파일로 리디렉션하고, exec <파일은 stdin을 파일 등으로 대체합니다. 평소와 동일하지만 exec를 사용합니다 (자세한 내용은 man exec 참조).
Loki

업데이트 섹션에서 다음과 같이 FD 3과 4도 닫아야합니다. exec 1>&3 2>&4 3>&- 4>&-
Gurjeet Singh

답변:


173

업데이트 된 질문을 해결합니다.

#...part of script without redirection...

{
    #...part of script with redirection...
} > file1 2>file2 # ...and others as appropriate...

#...residue of script without redirection...

중괄호 '{...}'는 I / O 리디렉션 단위를 제공합니다. 중괄호는 명령이 나타날 수있는 위치 즉, 줄의 시작 부분이나 세미콜론 뒤에 나타납니다. ( 예, 더 정확하게 만들 수 있습니다. 떨고 싶다면 알려주세요. )

표시된 리디렉션으로 원래 stdout 및 stderr을 보존 할 수는 있지만, 나중에 스크립트를 유지 관리해야하는 사람들이 위에 표시된대로 리디렉션 된 코드의 범위를 지정하면 진행 상황을 이해하는 것이 더 간단합니다.

Bash 매뉴얼의 관련 섹션은 그룹화 명령I / O 리디렉션 입니다. POSIX 쉘 스펙의 관련 섹션은 복합 명령I / O 리디렉션 입니다. Bash에는 몇 가지 추가 표기법이 있지만 POSIX 셸 사양과 비슷합니다.


3
원본 디스크립터를 저장하고 나중에 복원하는 것보다 훨씬 명확합니다.
Steve Madsen

23
나는 이것이 실제로하고있는 것을 이해하기 위해 인터넷 검색을해야 했으므로 공유하고 싶었습니다. 중괄호는 "코드 블록" 이되어 익명 함수를 생성합니다 . 그런 다음 코드 블록의 모든 출력을 리디렉션 할 수 있습니다 ( 해당 링크의 예 3-2 참조 ). 중괄호 서브 쉘을 시작 하지 않지만 서브 쉘 에서 괄호를 사용하여 유사한 I / O 리디렉션 수행 할 수 있습니다.
chris

3
나는이 솔루션을 다른 솔루션보다 더 좋아합니다. I / O 리디렉션에 대해 가장 기본적으로 이해하고있는 사람조차도 무슨 일이 일어나고 있는지 이해할 수 있습니다. 또한 더 장황합니다. 그리고 Pythoner로서 나는 장황한 것을 좋아합니다.
John Red

더 낫다 >>. 어떤 사람들은 습관이 >있습니다. 넘기는 것보다 항상 안전하고 더 좋습니다. 누군가 표준 복사 명령을 사용하여 일부 데이터를 동일한 대상으로 내보내는 응용 프로그램을 작성했습니다.
neverMind9

이 앱은 ccc.bmw71 (.pro) (이전의 "배터리 모니터 위젯"인 3C 배터리 모니터 위젯)는 항상 배터리 내역을 /sdcard/bmw_history.txt로 내 보냅니다. 이미 존재하는 경우 덮어 쓰십시오! 이로 인해 실수로 일부 배터리 기록이 손실되었습니다. 30 일에서 매우 높은 수로 제한을 설정하여 무효화하고 다시 30으로 설정했습니다. 기존 백업을 가져 오기 전에 현재 배터리 기록을 내보내고 싶었습니다. 그런 다음 일어났다.
neverMind9

175

일반적으로이 중 하나를 스크립트 상단 또는 상단에 배치합니다. 명령 줄을 구문 분석하는 스크립트는 구문 분석 후 리디렉션을 수행합니다.

stdout을 파일로 보내기

exec > file

stderr와 함께

exec > file                                                                      
exec 2>&1

stdout과 stderr을 파일에 추가하십시오.

exec >> file
exec 2>&1

조나단 레플러는 그의 주석에서 언급 한 :

exec두 개의 별도 작업이 있습니다. 첫 번째는 현재 실행중인 쉘 (스크립트)을 새로운 프로그램으로 교체하는 것입니다. 다른 하나는 현재 셸에서 I / O 리디렉션을 변경하는 것입니다. 에 대한 인수가 없어서 구별됩니다 exec.


7
나는 또한 그 끝에 2> & 1을 추가한다고 말하면 stderr도 잡히게됩니다. :-)
Chris Jester-Young

7
이것들을 어디에 두나요? 스크립트 상단에?
colan

4
이 솔루션을 사용하면 스크립트가 종료하는 리디렉션도 재설정해야합니다. Jonathan Leffler의 다음 답변은 이런 의미에서 "실패"입니다.
Chuim

6
@JohnRed : exec두 개의 개별 작업이 있습니다. 하나는 동일한 프로세스를 사용하여 현재 스크립트를 다른 명령으로 바꾸는 것입니다. 다른 명령을 인수로 지정하면 execI / O 리디렉션을 조정할 수 있습니다. 다른 작업은 현재 셸 스크립트에서 I / O 리디렉션을 바꾸지 않고 변경하는 것입니다. 이 표기법은에 대한 인수로 명령이없는 것으로 구별됩니다 exec. 이 답변의 표기법은 "I / O 전용"변형입니다. 리디렉션 만 변경하고 실행중인 스크립트를 대체하지 않습니다. (이 set명령은 다목적입니다.)
Jonathan Leffler

18
exec > >(tee -a "logs/logdata.log") 2>&1로그를 화면에 인쇄하고 파일에 기록합니다
shriyog

32

전체 스크립트를 다음과 같은 함수로 만들 수 있습니다.

main_function() {
  do_things_here
}

그런 다음 스크립트 끝에 다음이 있습니다.

if [ -z $TERM ]; then
  # if not run via terminal, log everything into a log file
  main_function 2>&1 >> /var/log/my_uber_script.log
else
  # run via terminal, only output to screen
  main_function
fi

또는 각 실행마다 모든 것을 로그 파일에 기록하고 간단하게 다음을 수행하여 stdout에 출력 할 수 있습니다.

# log everything, but also output to stdout
main_function 2>&1 | tee -a /var/log/my_uber_script.log

main_function >> /var/log/my_uber_script.log 2> & 1
펠리페 알바레즈

나는 그런 파이프에서 main_function을 사용하는 것을 좋아합니다. 그러나이 경우 스크립트는 원래 반환 값을 반환하지 않습니다. bash의 경우 'exit $ {PIPESTATUS [0]}'을 사용하여 종료해야합니다.
rudimeier

7

원래 stdout 및 stderr을 저장하려면 다음을 사용할 수 있습니다.

exec [fd number]<&1 
exec [fd number]<&2

예를 들어 다음 코드는 "walla1"및 "walla2"를 로그 파일 ( a.txt)로, "walla3"을 stdout으로, "walla4"를 stderr로 인쇄합니다.

#!/bin/bash

exec 5<&1
exec 6<&2

exec 1> ~/a.txt 2>&1

echo "walla1"
echo "walla2" >&2
echo "walla3" >&5
echo "walla4" >&6

1
일반적으로, 사용하기 좋을 것이다 exec 5>&1exec 6>&2출력을위한 출력 재 표기보다는 입력 재 표기법. 스크립트가 터미널에서 실행될 때 표준 입력도 쓸 수 있고 역사적인 출력으로 인해 표준 출력과 표준 오류를 모두 읽을 수 있기 때문에 (또는 '바이스'입니까?) 읽기 및 쓰기와 동일한 열린 파일 설명 이 세 가지 표준 I / O 파일 설명자 모두에 사용됩니다.
Jonathan Leffler

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.