파이프로“티”를 사용하는 동안 파일에 stderr를 쓰려면 어떻게해야합니까?


541

내가 사용하는 방법을 알고 tee출력 (작성 STDOUT중) aaa.sh에을 bbb.out여전히 단말기에 표시하는 동안 :

./aaa.sh | tee bbb.out

이제 STDERR라는 파일 ccc.out을 계속 표시하면서 어떻게합니까?


2
명확히하기 위해-stderr이 파일뿐만 아니라 화면으로 이동하겠습니까?
Charles Duffy

나는 그것을 명확히하기 위해 게시물을 편집 할 것입니다. lhunath의 솔루션으로 충분하다고 생각합니다. 모든 도움을 주셔서 감사합니다!
jparanich

답변:


784

터미널에 STDERR 및 STDOUT이 계속 표시되기를 원한다고 가정합니다. 조쉬 켈리 (Josh Kelley)의 답변을 얻을 수는 있지만 tail로그 파일을 매우 해킹하고 어리석게 출력하는 백그라운드를 유지합니다 . exra FD를 어떻게 유지하고 나중에 죽여서 정리해야하는지 기술적으로 기술적으로해야합니다 trap '...' EXIT.

이를 수행하는 더 좋은 방법이 있으며 이미 발견했습니다 tee..

stdout에 사용하는 대신 stdout에 대한 티와 stderr에 대한 티를 사용하십시오. 당신은 이것을 어떻게 성취 할 것입니까? 프로세스 대체 및 파일 리디렉션 :

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

그것을 나누고 설명합시다.

> >(..)

>(...)(프로세스 대체) FIFO를 작성하고 tee청취하십시오. 그런 다음 >(파일 리디렉션)을 사용 하여 STDOUT을 command처음 tee듣고 있는 FIFO 로 리디렉션합니다 .

두 번째도 마찬가지입니다.

2> >(tee -a stderr.log >&2)

프로세스 대체를 다시 사용 tee하여 STDIN에서 읽고 프로세스를 덤프 하는 프로세스 를 만듭니다 stderr.log. tee입력을 STDOUT에 다시 출력하지만 입력이 STDERR이므로 tee의 STDOUT을 STDERR로 다시 리디렉션하려고 합니다. 그런 다음 파일 리디렉션을 사용하여 command의 STDERR을 FIFO의 입력 ( teeSTDIN) 으로 리디렉션 합니다.

http://mywiki.wooledge.org/BashGuide/InputAndOutput을 참조하십시오 .

프로세스 대체는 (POSIX 또는 Bourne) bash과 달리 쉘로 선택하는 보너스로 얻는 정말 멋진 것들 중 하나입니다 sh.


에서 sh수동으로 작업을 수행해야합니다.

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

5
나는 이것을 시도했다 : $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)그것은 효과가 있지만 입력을 기다린다. 이런 일이 발생하는 간단한 이유가 있습니까?
저스틴

1
@SillyFreak 나는 당신이하고 싶은 일이나 문제가 무엇인지 이해하지 못합니다. 에코 테스트; exit는 stdout에서 출력을 생성하지 않으므로 err은 비어 있습니다.
lhunath

1
그 의견에 감사드립니다. 나는 나중에 논리적 오류가 무엇인지 알아 냈습니다. 대화 형 쉘로 호출되면 bash는 명령 프롬프트를 인쇄하고 종료를 stderr에 에코합니다. 그러나 stderr이 리디렉션되면 bash는 기본적으로 비 대화식으로 시작합니다. 비교 /bin/bash 2> err/bin/bash -i 2> err
바보 괴물에게

15
"보고 믿는"사람들을위한 빠른 테스트 :(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
Matthew Wilcoxson

2
와우 . 좋은 답변 : "분할하고 설명합시다"+1
jalanb

672

왜 간단하지 않습니까?

./aaa.sh 2>&1 | tee -a log

이것은 단순히 리디렉션 stderr하기 stdout, 티 에코 그래서 모두가 로그인 화면에 할 수 있습니다. 다른 솔루션 중 일부가 실제로 복잡해 보이므로 뭔가 빠졌을 수 있습니다.

참고 : bash 버전 4부터 다음에|& 대한 약어로 사용할 수 있습니다 2>&1 |.

./aaa.sh |& tee -a log

85
stdout (채널 1)과 stderr (채널 2) 모두 동일한 파일 (stdout과 sterr의 혼합을 포함하는 단일 파일)에 기록하려는 경우 제대로 작동합니다. 다른보다 복잡한 솔루션을 사용하면 stdout과 stderr을 두 개의 다른 파일 (각각 stdout.log와 stderr.log)로 분리 할 수 ​​있습니다. 때때로 그것은 중요하지만 때로는 그렇지 않습니다.
Tyler Rick

19
다른 솔루션은 많은 경우에 필요한 것보다 훨씬 더 복잡합니다. 이것은 나를 위해 완벽하게 작동합니다.
dkamins

13
이 방법의 문제점은 aaa.sh 프로세스에서 종료 / 상태 코드를 잃어 버리는 것입니다. 이는 메이크 파일에서 사용할 때 중요 할 수 있습니다. 허용되는 답변에이 문제가 없습니다.
Stefaan 2016 년

9
병합 된 stdout / stderr가 마음에 들지 않으면 ./aaa.sh |& tee aaa.logbash에서 작동합니다.
jfs

5
와 내가 명령 체인을 붙일 경우 종료 상태를 유지할 수 있다고 생각 @Stefaan set -o pipefail다음 ;또는 &&경우에 나는 잘못이 아니에요.
David

59

이것은 사람들이 구글을 통해 이것을 찾는 데 유용 할 수 있습니다. 시도하려는 예제의 주석을 해제하십시오. 물론 출력 파일 이름을 자유롭게 바꾸십시오.

#!/bin/bash

STATUSFILE=x.out
LOGFILE=x.log

### All output to screen
### Do nothing, this is the default


### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1


### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1


### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)


### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)


### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}


### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)


### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}


### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)


echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}

6
아니요, exec가 설명을 사용할 수 있다고 생각합니다. exec >즉, 파일 디스크립터의 대상을 특정 대상으로 이동합니다. 기본값은 1 exec > /dev/null이므로이 세션에서 stdout의 출력을 지금부터 / dev / null로 이동합니다. 이 세션의 현재 파일 디스크립터는를 수행하여 볼 수 있습니다 ls -l /dev/fd/. 시도 해봐! 그런 다음 발행시 발생하는 상황을 참조하십시오. exec 2>/tmp/stderr.log.또한 exec 3>&1번호 3으로 새 파일 디스크립터를 작성하여 파일 디스크립터 1의 대상으로 경로 재 지정하십시오. 예에서 대상은 명령이 발행되었을 때의 화면이었습니다.
드럼 파이어

화면과 별도의 파일에 stdout과 stderr을 표시하는 예제는 훌륭합니다! 고마워요!
Marcello de Sales

21

stderr을 파일로 리디렉션하려면 stdout을 화면에 표시하고 stdout을 파일에 저장하십시오.

./aaa.sh 2> ccc.out | 티 ./bbb.out

편집 : stderr 및 stdout을 화면에 표시하고 파일에 모두 저장하려면 bash의 I / O 리디렉션을 사용할 수 있습니다 .

#!/bin/bash

# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out

# Also print the contents of this file to screen.
tail -f ccc.out &

# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out

# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1

1
사용자는 stderr이 명시 적으로 지정되지 않았지만 파일 외에도 콘솔로 이동하기를 원합니다.
찰스 더피

2
나는 더 명확해야했고, 스크린에 stderr을 원했다. 나는 여전히 Josh Kelley의 솔루션을 즐겼지만 lhunath가 내 요구에 더 잘 맞는다는 것을 알았습니다. 고마워요!
jparanich

18

즉, stdout을 한 필터 ( tee bbb.out) 에 파이프 하고 stderr를 다른 필터 ( tee ccc.out) 에 파이프하려고합니다 . stdout 이외의 다른 것을 다른 명령으로 파이프하는 표준 방법은 없지만 파일 설명자를 저글링하여 해결할 수 있습니다.

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

표준 오류 스트림을 잡는 방법 (stderr) 도 참조하십시오 . 그리고 언제 추가 파일 디스크립터를 사용한다?

bash (및 ksh 및 zsh)에서는 대시와 같은 다른 POSIX 셸에서는 프로세스 대체를 사용할 수 있습니다 .

./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)

bash에서는이 명령 이 여전히 실행 되더라도 (ksh 및 zsh가 하위 프로세스를 기다리 ./aaa.sh더라도) 완료 되 자마자이 명령이 리턴된다는 점에 유의하십시오 tee. 다음과 같은 작업을 수행하면 문제가 될 수 있습니다 ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. 이 경우 대신 파일 디스크립터 저글링 또는 ksh / zsh를 사용하십시오.


4
이것은 stdout / stderr 스트림을 그대로 유지할 수있는 유일한 대답 인 것 같습니다 (예 : 병합하지 않음). 단!
user1338062

sh프로세스 대체를 사용할 수없는 cron 작업에 유용한 가장 간단한 방법입니다 .
Roger Dueck

13

bash를 사용하는 경우 :

# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect

# Redirect standard error and out together
% cmd >stdout-redirect 2>&1

# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2

신용 (내 머리 위에서 대답하지 않음)은 여기에 있습니다 : http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html


5

필자의 경우 stdout 및 stderr을 파일로 리디렉션하는 동안 스크립트가 명령을 실행 중이었습니다.

cmd > log 2>&1

오류가 발생했을 때 오류 메시지를 기반으로 몇 가지 조치를 취하도록 업데이트해야했습니다. 물론 dup을 제거하고 2>&1스크립트에서 stderr을 캡처 할 수는 있지만 오류 메시지는 참조를 위해 로그 파일로 이동하지 않습니다. @lhunath에서 허용 대답은 동일한 기능을 수행하도록되어 있지만, 리디렉션 stdoutstderr내가 원하는하지 않은, 다른 파일에 있지만, 그것은 나를 정확한 해결책을 마련하는 데 도움이된다는 사실을 내가 필요 :

(cmd 2> >(tee /dev/stderr)) > log

위로, 로그는 모두의 사본을해야합니다 stdout그리고 stderr내가 캡처 할 수 있습니다 stderr에 대해 걱정할 필요없이 내 스크립트 stdout.


4

다음은 프로세스 대체가 불가능한 KornShell (ksh)에서 작동합니다.

# create a combined(stdin and stdout) collector
exec 3 <> combined.log

# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3

# cleanup collector
exec 3>&-

여기서 실제 트릭 2>&1 1>&3은 우리의 경우에서 stderrto stdout로 리디렉션하고 stdoutto descriptor로 리디렉션하는 순서입니다 3. 이 시점에서 stderrstdout아직 결합되지 않았습니다.

실제로 stderr(as stdin)는 설명자 3에 tee로그인 stderr.log하고 설명자 3으로 리디렉션됩니다.

그리고 기술자 3combined.log항상 그것을 기록 하고 있습니다. 따라서 와를 combined.log모두 포함합니다 .stdoutstderr


맵시 있는! 좋은 옵션이기 때문에 과소 평가되는 동정. 나는 KSH, btw :)
runlevel0

2

당신이 사용하는 경우 zsh을을 당신도 필요가 없습니다, 당신은 여러 리디렉션을 사용할 수 있습니다 tee:

./cmd 1>&1 2>&2 1>out_file 2>err_file

여기서는 각 스트림을 자신 대상 파일 로 리디렉션 합니다.


전체 예

% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err

이를 위해서는 MULTIOS옵션을 설정 해야합니다 (기본값).

MULTIOS

여러 리디렉션을 시도 할 때 암시 적 tee또는을 수행하십시오 cat( 리디렉션 참조 ).

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