stderr 및 stdout을 각각 bash 스크립트의 함수로 리디렉션

stderr 및 stdout을 각각 bash 스크립트의 함수로 리디렉션

나는 노력하고있다래퍼 스크립트cronjob 출력을 기록합니다.

나는 몇 가지 목표를 가지고 있습니다:

  • stderr 및 stdout은 서로 다른 우선순위 및 접두사 라벨을 사용하여 기록되어야 합니다.
  • 또한 래핑된 명령의 stderr을 래퍼 스크립트의 stderr로 출력해야 합니다(cron으로 캡처하고 이메일로 경고함).
  • 라인 순서는 항상 유지되어야 합니다

지금까지 두 가지 문제가 있는 것 같습니다.

  1. stderr과 stdout을 별도로 로깅 기능으로 리디렉션하는 방법을 잘 모르겠습니다. 지금까지 시도한 모든 것은 NULL 이외의 다른 것을 리디렉션하지 못했습니다. 내 생각에는 리디렉션이 내 장점 중 하나가 아닙니다.(해결됨, 댓글 참조)
  2. 내 현재 접근 방식 { $_EXEC $_ARGS 2>&1 1>&7 7>&- | journaldlog error; } 7>&1 1>&2 | journaldlog info은 로깅 기능을 두 번(stderr에 대해 한 번, stdout에 대해 한 번) 실행하는 것 같습니다. 이렇게 하면 줄 순서가 유지되지 않습니다. 오히려 출력 라인당 한 번씩 함수를 실행하고 싶습니다.

bash에서 이 작업을 포기하고 Perl을 사용해 봐야 할까요? 나는 Perl 경험이 결국 다시 돌아올 것이라고 확신합니다.

나는 다양한 것을 시도했습니다. 이들 중 대부분은 온라인, 특히 Stack Exchange에서 찾을 수 있습니다. 나는 솔직히 내가 무엇을 시도했는지 기억이 나지 않습니다. 이 시점에서는 단지 흐릿할 뿐입니다. 그리고 bash 문서는 별로 도움이 되지 않습니다.

답변1

그냥 사용할 수 있습니다프로세스 교체리디렉션에서 직접:

gen > >(one) 2> >(two)

그러면 표준 출력이 gen표준 입력으로 제공되고 one표준 오류가 gen입력으로 제공됩니다 two. 실행 가능한 명령이나 함수가 될 수 있습니다. 제가 사용하는 기능은 다음과 같습니다.

one() {
    while read line
    do
        echo $'\e[31m'"$line"$'\e[22m'
    done
}

two() {
    while read line
    do
        echo $'\e[32m'"$line"$'\e[22m'
        echo "$line" >&2
    done
}

입력 라인을 각각 빨간색과 녹색으로 출력하고 two일반 표준 오류에도 기록합니다. 루프 내에서 원하는 것은 무엇이든 할 수 있고 입력을 다른 프로그램이나 필요한 모든 것으로 보낼 수 있습니다.


알아채다이 시점에 이르면 "줄 순서 유지"와 같은 실제적인 기능은 없습니다.- 두 개의 독립적인 스트림과 독립적인 프로세스입니다. 스케줄러는 한동안 한 프로세스를 실행한 다음 다른 프로세스를 실행하고 데이터는 읽을 때까지 커널 파이프 버퍼에 보관됩니다. 나는 테스트를 위해 홀수를 stdout 및 stderr로 출력하는 함수를 사용해 왔으며 일반적으로 각 함수에서 연속으로 12개 이상을 실행합니다.

터미널에 표시된 것과 가까운 주문을 얻을 수 있지만 Bash의 주문은 아닐 수도 있습니다. 교환 프로그램 사용법pipe그리고select시퀀스를 재구성하는 것이 가능합니다(비록 표시된 것과 동일하다고 보장할 수는 없지만 동일한 이유로 프로세스가 한동안 예약되지 않을 수 있으며 일단 백로그가 발생하면 먼저 무슨 일이 일어났는지 알 수 있는 방법이 없습니다1 ). . 순서가 중요한 경우 다른 접근 방식이 필요하며 기본 실행 파일의 공동 작업도 필요할 수 있습니다. 이것이 어려운 요구 사항이라면 재고해 보는 것이 좋습니다.

1 파이프 버퍼링을 제어하는 ​​플랫폼별 방법이 있을 수도 있지만 이 방법도 충분히 도움이 되지는 않습니다. Linux에서는 버퍼 크기를 시스템 페이지 크기로 줄일 수 있지만 더 낮출 수는 없습니다. 나는 즉시 쓰기를 강제할 수 있는(또는 읽을 때까지 차단할 수 있는) 기능을 모릅니다. 그럼에도 불구하고 소스에서 stdio 스트림으로 버퍼링될 가능성이 높으므로 정렬을 볼 수 없습니다.

답변2

이것은 stderr과 stdout을 각각 함수로 리디렉션하는 데 사용하는 방법입니다.

tag() { awk '$0="['$1'] "$0; fflush();' ; }

{
    {
        echo std1; sleep 0.1;
        echo error2 >&2; sleep 0.1;
        echo error3 >&2; sleep 0.1;
        echo std4; sleep 0.1;
    } 2>&1 1>&3 | tag STDERR 1>&2 ;
} 3>&1 | tag STDOUT 3>&- ;

그러면 다음과 같은 출력이 제공됩니다.

[STDOUT] std1
[STDERR] error2
[STDERR] error3
[STDOUT] std4

내 bash 및 ksh (93u+)에서 작동합니다.

이 경우 줄 순서가 유지되도록 명령 사이에 "sleep 0.1"을 추가했습니다.

답변3

캡쳐하는 방식이에요표준 출력그리고표준 에러다른 명령 파이프라인으로.

#!/bin/bash
#
exec 3>&1 4>&2

loggerForStdout() {
    # FD 3 is the original stdout
    local x
    while IFS= read -r x; do printf "STDOUT\t%s\n" "$x"; done >&3
}
loggerForStderr() {
    # FD 4 is the original stderr
    local x
    while IFS= read -r x; do printf "STDERR\t%s\n" "$x"; done >&4
}


( (
    # This is where your command would be put
    # e.g. stdbuf -oL -eL rsync -a /from /to
    #
    echo this is stdout; echo this is stderr >&2

) | loggerForStdout ) 2>&1 | loggerForStderr

를 사용해도 잘 모르겠습니다 stdbuf -oL -eL.

대체 로거가 있는 경우 처음 세 줄을 생략할 수 있습니다. 이는 코드를 완전하고 독립적인 예제로 만들기 위해서만 존재합니다.

관련 정보