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

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

거의 다 탐색해 봤어모두 쓸 수 있는 비슷한 질문, 소용이 없습니다.

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

stdout 및 stderr 라인을 생성하는 무인 스크립트를 실행하고 이를 캡처하고 싶습니다.터미널 에뮬레이터에 표시된 정확한 순서를 따르세요.그런 다음 "STDERR:" 및 "STDOUT:"과 같은 접두사를 추가합니다.

나는 파이프와 epoll 기반 방법을 사용해 보았지만 성공하지 못했습니다. 나는 이 분야의 전문가는 아니지만 해결책은 pty 사용법에 있다고 생각합니다. 소스코드도 봤는데그놈용 VTE, 그러나 이것은 별로 효과가 없었습니다.

이상적으로는가다이 작업을 수행하기 위해 Bash 대신 나는 그것을 할 수 없었습니다. 버퍼링으로 인해 파이프가 올바른 라인 순서를 유지하는 것이 자동으로 금지되는 것 같습니다.

비슷한 일을 할 수 있는 사람이 있나요? 아니면 이것이 불가능합니까? 내 생각에 터미널 에뮬레이터가 이것을 할 수 있다면, 그럴 수 없을 것입니다. 아마도 PTY를 다르게 처리하는 작은 C 프로그램을 만드는 것이 아닐까요?

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

노트:알아요스트라이디드그러나 Bash 스크립트에서는 작동하지 않으며 접두사를 추가하기 위해 쉽게 편집할 수 없습니다(기본적으로 여러 시스템 호출을 래핑하기 때문입니다).

고쳐 쓰다:다음 두 가지 사항을 추가했습니다.

(일관적인 결과를 보여주기 위해 제가 제공한 예제 스크립트에 1초 미만의 무작위 지연을 추가할 수 있습니다.)

고쳐 쓰다:이 문제에 대한 해결책도 해결될 것입니다.이것은 또 다른 질문입니다, @Gilles가 지적했듯이. 그러나 나는 다음과 같은 결론에 도달했습니다.불가능한여기저기서 요청하는 대로 수행하세요. 두 개의 스트림을 사용하는 경우 2>&1pty/pipe 수준에서 올바르게 병합되지만 스트림을 개별적으로 올바른 순서로 사용하려면 실제로 다음 방법을 사용해야 합니다.스트라이디드다음과 같이 볼 수 있는 시스템 호출 후크가 포함됩니다.더러운모든 면에서.

누구든지 위의 요점을 반박할 수 있다면 기꺼이 질문을 업데이트하겠습니다.

답변1

코루틴을 사용할 수도 있습니다. 마크업을 실행하는 두 개의 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. 때때로 몇 줄을 바꾸지 않을 것이라는 보장은 없습니다. 모두 공동 프로세스의 일정에 따라 다릅니다. 사실, 언젠가는 그런 일이 일어날 것이 거의 확실합니다. 즉, 순서가 엄격하게 동일하게 유지되면 두 프로세스의 데이터가 동일한 프로세스에서 처리되어야 합니다 stderr. stdin그렇지 않으면 커널 스케줄러가 이를 망칠 수 있습니다.

    질문을 올바르게 이해했다면 이는 두 스트림을 하나의 프로세스로 리디렉션하도록 셸에 지시해야 함을 의미합니다(이 작업은 완료될 수 있음을 이해합니다). 문제는 프로세스가 무엇을 먼저 수행할지 결정하기 시작할 때 시작됩니다. 프로세스는 두 개의 데이터 소스를 폴링해야 하며 어느 시점에서는 하나의 스트림이 처리되고 데이터가 완료되기 전에 두 스트림 모두에 도달하는 상태가 됩니다. 이것이 무너지는 곳입니다. 이는 또한 이와 같은 출력 시스템 호출을 래핑하는 것이 stderred원하는 결과를 얻을 수 있는 유일한 방법일 수 있음을 의미합니다(그리고 그런 경우에도 다중 프로세서 시스템에서 다중 스레드가 되면 문제가 발생할 수 있습니다).

공동 프로세스에 관해서는 Stéphane의 글을 꼭 읽어보세요.Bash에서 coproc 명령을 사용하는 방법은 무엇입니까?깊은 통찰력을 얻기 위해서입니다.

답변2

방법 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

여기에서는 STDOUT에 쓰고 a10초 동안 대기한 다음 출력을 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

타임스탬프 부분의 출력 형식을 제어할 수 있지만 더 많은 부분은 제어할 수 없습니다. 그러나 출력은 찾고 있는 것과 유사하므로 계산서에 맞을 수도 있습니다.

답변3

스크립트가 기본적으로 출력을 두 개의 개별 파이프로 분할한다는 점을 감안할 때 이러한 파이프에 연결된 단일 프로그램이라도 어떤 줄도 교체되지 않았음을 100% 확신하는 것은 물리적으로 불가능합니다. 어느 시점에서는 교대로 비차단 읽기를 수행해야 합니다. 다음에 읽을 파이프를 결정해야 합니다. 즉, 터미널 에뮬레이터조차도 줄의 순서를 제대로 유지하지 못하는 것 같습니다.

관련 정보