정렬 된 STDOUT / STDERR을 캡처하고 타임 스탬프 / 접두사를 추가하는 방법은 무엇입니까?


25

나는 거의 모든 가능한 비슷한 질문 을 탐색했지만 아무 소용이 없습니다.

문제를 자세하게 설명하겠습니다.

무인 스크립트를 실행하면 표준 출력 및 표준 오류 행이 생성 될 수 있습니다 . 터미널 에뮬레이터에서 표시 한대로 정확한 순서로 캡처 한 다음 "STDERR :"및 "STDOUT :"과 같은 접두사를 추가하고 싶습니다.

나는 파이프와 epoll 기반 접근 방식을 사용해 보았습니다. 나는 마스터가 아니지만 솔루션이 pty 사용법이라고 생각합니다. 나는 또한 Gnome VTE 의 소스 코드를 들여다 보았지만 그다지 생산적이지는 않았다.

이상적으로 는 Bash 대신 Go 를 사용 하여이 작업을 수행하지만 수행 할 수 없었습니다. 파이프로 인해 버퍼링으로 인해 올바른 라인 순서를 유지하지 못하는 것처럼 보입니다.

누군가 비슷한 것을 할 수 있었습니까? 아니면 불가능한가요? 터미널 에뮬레이터가 그렇게 할 수 있다면 PTY를 다르게 처리하는 작은 C 프로그램을 만드는 것이 아닐까요?

이상적으로는 비동기 입력을 사용 하여이 2 개의 스트림 (STDOUT 및 STDERR)을 읽은 다음 필요에 따라 다시 인쇄하지만 입력 순서는 중요합니다!

참고 : stderred를 알고 있지만 Bash 스크립트에서는 작동하지 않으며 접두사를 추가하기 위해 쉽게 편집 할 수 없습니다 (기본적으로 많은 syscall을 래핑하기 때문에).

업데이트 : 두 요점 아래에 추가

(일관된 결과를 증명하기 위해 제공 한 샘플 스크립트에 1 초 미만의 임의 지연이 추가 될 수 있음)

업데이트 : 이 질문에 대한 해결책 은 @Gilles가 지적 했듯이이 다른 질문을 해결할 것입니다. 그러나 나는 여기 저기에서 요구 것을 할 수 없다는 결론에 도달했습니다 . 2>&1두 스트림 을 모두 pty / pipe 수준에서 올바르게 병합 할 때 스트림을 개별적이고 올바른 순서로 사용하려면 실제로 시스템 콜 후크를 포함하고 여러 가지 방법 으로 더티 로 보일 수있는 stderred 의 접근 방식을 사용해야 합니다 .

누군가 위의 내용을 거부 할 수 있다면이 질문을 업데이트하기를 열망합니다.


1
이것이 당신이 원하는 것이 아닙니까? stackoverflow.com/questions/21564/…
slm

OP는 다른 문자열을 다른 스트림 앞에 추가해야하기 때문에 @slm은 아닐 것입니다 .
peterph 2014 년

주문이 왜 중요한지 공유 할 수 있습니까? 어쩌면 문제를 해결할 다른 방법이있을 수도 있습니다.
peterph

@ peterph 그것은 전제 조건입니다. 일관된 출력을 얻을 수 없다면 그것을 읽기보다는 / dev / null로 보내서 혼동합니다 :) 2> & 1은 예를 들어 순서를 유지하지만 종류는 허용하지 않습니다 이 질문에서 사용자 정의의 정의
Deim0s

답변:


12

코 프로세스를 사용할 수 있습니다. 지정된 명령의 두 출력을 두 sed인스턴스 (하나 stderr는 다른 하나)에 공급하는 간단한 래퍼 ( stdout태깅).

#!/bin/bash
exec 3>&1
coproc SEDo ( sed "s/^/STDOUT: /" >&3 )
exec 4>&2-
coproc SEDe ( sed "s/^/STDERR: /" >&4 )
eval $@ 2>&${SEDe[1]} 1>&${SEDo[1]}
eval exec "${SEDo[1]}>&-"
eval exec "${SEDe[1]}>&-"

몇 가지 사항에 유의하십시오.

  1. 그것은 많은 사람들 (나를 포함하여)에게 마법의 주문입니다-이유가 있습니다 (아래 링크 된 답변 참조).

  2. 때때로 몇 줄을 교체하지 않을 것이라는 보장은 없습니다. 모두 코 프로세스의 일정에 따라 다릅니다. 실제로, 어느 시점에서 그렇게 할 것이 거의 보장됩니다. 즉 엄격하게 동일한 순서를 유지하는 경우, 당신은 모두의 데이터를 처리 할 필요가 말했다 stderrstdin달리 커널 스케줄러 (그리고 것) 그것의 혼란을 만들 수 있습니다, 같은 프로세스에 있습니다.

    문제를 올바르게 이해하면 쉘이 두 스트림을 하나의 프로세스로 리디렉션하도록 지시해야 함을 의미합니다 (AFAIK 수행 가능). 프로세스가 먼저 무엇을해야할지 결정하기 시작할 때 문제가 시작됩니다. 두 데이터 소스를 모두 폴링해야하고 어느 시점에서 하나의 스트림을 처리하고 완료되기 전에 데이터가 두 스트림에 도달하는 상태가되어야합니다. 그리고 그것이 정확히 고장난 곳입니다. 또한 출력 syscall을 래핑하는 stderred것이 원하는 결과를 얻는 유일한 방법 일 수 있습니다 (그리고 다중 프로세서 시스템에서 무언가가 멀티 스레딩되면 문제가 발생할 수 있음).

코 프로세스가 있는 한 Bash에서 coproc 명령어떻게 사용합니까?에서 Stéphane의 훌륭한 답변을 읽으십시오 . 깊이있는 통찰력.


귀하의 답변에 대해 @peterph에게 감사드립니다. 그러나 주문을 보존하는 방법을 구체적으로 찾고 있습니다. 참고 : 사용하는 프로세스 대체 ( ./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpected스크립트 복사 / 붙여 넣기로 인해) 때문에 통역사가 bash 여야한다고 생각합니다.
Deim0s

매우 가능성이 있으므로, 나는 그것을 실행 bash/bin/sh(내가 거기에 있었다 왜 확인).
peterph

스트림 믹스 업이 발생할 수있는 위치와 관련하여 질문을 약간 업데이트했습니다.
peterph

1
eval $@꽤 버그가 있습니다. 사용 "$@"당신이 정확한 명령 줄로 인수를 실행하려는 경우 - 층 추가 eval하면 파일 이름 또는으로 제어하지 않는 다른 내용을 전달하는 경우 해석은 어려운 예측 (그리고 잠재적으로 악성의 무리에 던져 인수) 동작 및 더 인용하지 않음 (공백이있는 이름을 여러 단어로 분할하고 이전에 리터럴로 인용 된 경우에도 글로브를 확장 함)
Charles Duffy

1
또한 동시 처리가 가능한 동시 처리 bash에서는 변수에 이름이 지정된 파일 디스크립터를 닫을 필요 가 없습니다 eval. exec {SEDo[1]}>&-(예,의 부족 그대로 작동 $전과가 {고의적이었다).
Charles Duffy

5

방법 # 1. 파일 디스크립터와 awk 사용

이 SO Q & A의 솔루션을 사용하여 이와 같은 것은 어떻습니까? 타임 스탬프를 텍스트 줄 앞에 추가하는 Unix 유틸리티가 있습니까? 그리고이 SO Q & A 제목 : 파이프 STDOUT과 STDERR을 쉘 스크립트의 두 개의 다른 프로세스에? .

접근

1 단계 : Bash에서 호출 될 때 타임 스탬프 메시지를 수행하는 2 개의 함수를 만듭니다.

$ msgOut () {  awk '{ print strftime("STDOUT: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }
$ msgErr () {  awk '{ print strftime("STDERR: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }

2 단계 위와 같은 기능을 사용하여 원하는 메시지를 얻습니다.

$ { { { ...command/script... } 2>&3; } 2>&3 | msgErr; } 3>&1 1>&2 | msgOut

여기 a에서는 STDOUT에 쓰고 10 초 동안 휴면 한 다음 출력을 STDERR에 쓰는 예제를 작성 했습니다. 이 명령 시퀀스를 위의 구문에 넣으면 지정한대로 메시지가 표시됩니다.

$ { { echo a; sleep 10; echo >&2 b; } 2>&3 | \
    msgErr; } 3>&1 1>&2 | msgOut
STDERR: 2014-09-26 09:22:12 a
STDOUT: 2014-09-26 09:22:22 b

방법 # 2. 주석 출력 사용

원하는 작업을 수행 할 패키지 annotate-output의 일부 라는 도구가 있습니다 devscripts. 유일한 제한은 스크립트를 실행해야한다는 것입니다.

위의 예제 명령 시퀀스를 다음 mycmds.bash과 같은 스크립트에 넣으면 :

$ cat mycmds.bash 
#!/bin/bash

echo a
sleep 10
echo >&2 b

그런 다음 다음과 같이 실행할 수 있습니다.

$ annotate-output ./mycmds.bash 
09:48:00 I: Started ./mycmds.bash
09:48:00 O: a
09:48:10 E: b
09:48:10 I: Finished with exitcode 0

타임 스탬프 부분에 대해서는 출력 형식을 제어 할 수 있지만 그 이상은 아닙니다. 그러나 그것은 당신이 찾고있는 것과 비슷한 결과이므로 계산서에 맞을 수 있습니다.


1
불행히도 이것은 또한 일부 라인을 바꾸는 문제를 해결하지 못합니다.
peterph 2014 년

정확하게. 내 질문에 대한 답은 "불가능"하다고 생각합니다. stderred당신 과 함께하는 이벤트 는 선의 경계를 쉽게 결정할 수 없습니다 (시도 해킹 일 것입니다).
누군가이

방법 1의 2 단계에서는 앞에 다른 {가 올바르게 작동해야합니다.
Austin Hanson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.