데이터를 스크립트나 함수로 파이핑할 때 출력이 stdout으로 갈지 아니면 stderr로 갈지 결정하세요.

데이터를 스크립트나 함수로 파이핑할 때 출력이 stdout으로 갈지 아니면 stderr로 갈지 결정하세요.

데이터를 파이프하고 데이터의 "도착"을 표시하는 fd(stdout 또는 stderr)를 출력 앞에 추가할 수 있는 일반 함수나 스크립트를 만들려고 합니다. 내 Bash 기술은 기껏해야 평균 수준이고 { foo 2>&1 >&3 3>&- | bar 3>&-; } 3>&1.

이것이 가능한지, 그리고 그것을 달성하는 방법을 제안할 수 있는 사람이 있습니까?

본질적으로 나는 다음과 같은 작업을 수행하고 싶습니다.

$ { echo "foo" ; echo "bar" 1>&2 ; } | my_output_processor.sh
stdout: foo
stderr: bar 

나는 읽었다파이프 STDERR 및 STDOUT하지만 그것은 나에게 대답이 되지 않았습니다. 내 생각에 대답은https://unix.stackexchange.com/a/440439/307184단서가 포함되어 있을 수도 있지만 데이터 파이프라인이 아닌 실행 스크립트에서만 작동하는 것 같습니다.

내 macOS의 Bash 버전은 5.0입니다.

편집하다:@Glenn 아래 답변이 작동합니다! 나는 타이핑을 절약하고 오류를 방지하기 위해 몇 가지 작은 도우미 함수를 만들고 다음과 같이 테스트했습니다.

$ out(){ sed "s/^/out: /" ;} ; err(){ sed "s/^/err: /" 1>&2 ;}
$ { echo "good" ; echo "bad" >&2; } 2> >(err) 1> >(out)
err: bad
out: good

리디렉션 순서에 대한 후속 질문:2> >(err) 1> >(out)잘 작동하고 1> >(out) 2> >(err)잘 작동하지 않는 이유 는 무엇입니까? (고쳐 쓰다:아래 @RudiC의 답변 - 도우미 기능이 업데이트되어 이제 어떤 순서로든 작동합니다.

답변1

사용프로세스 교체파이프가 아님:

{ echo "foo" ; echo "bar" >&2; } 2> >(sed "s/^/err: /") > >(sed "s/^/out: /")
err: bar
out: foo

스크립트에서 이 작업을 수행하는 경우 다음 exec명령을 사용하여 스크립트 전체 기간 동안 리디렉션을 설정하세요.

bash -c '
    exec 2> >(sed "s/^/err: /") > >(sed "s/^/out: /")
    echo foo
    echo bar >&2
'
err: bar
out: foo

당신이 그렇게한다면

exec > >(sed "s/^/out: /") 2> >(sed "s/^/err: /")

그러면 출력은 다음과 같습니다.

out: foo
out: err: bar

나는 이것이 "작동하지 않는다"는 의미라고 생각합니다. 이는 기본적으로 표준 출력으로 이동하는 "err" 프로세스 교체의 출력이 이제 "out" 프로세스 하위 프로세스로 전달되기 때문입니다. 문제를 해결하려면 다음을 수행해야 합니다.

exec 3>&1 > >(sed "s/^/out: /") 2> >(sed "s/^/err: /" >&3)

기본 stdout을 가리키는 다른 파일 설명자(3)를 만든 다음 stderr를 리디렉션하여 fd3으로 인쇄합니다.

답변2

후속 질문은 실행 중에 스스로 대답합니다.

err기능은 표준 출력으로도 인쇄합니다. 분명히 첫 번째 형식에는 원래 표준 출력이 있고 두 번째 형식에서는 이미 "out() 함수로 리디렉션"을 사용하고 있습니다.

답변3

이는 bash뿐만 아니라 모든 표준 쉘에서 작동합니다.

{ cmd 2>&1 >&3 | sed 's/^/err: /' >&2; } 3>&1 | sed 's/^/out: /'

편의상 이를 함수로 만들 수 있습니다.

# usage louterr cmd [args ...]
louterr(){ { "$@" 2>&1 >&3 | sed 's/^/err: /' >&2; } 3>&1 | sed 's/^/out: /'; }

cmd(){ echo ERR >&2; echo OUT; }
louterr cmd
out: OUT
err: ERR
louterr louterr louterr cmd
err: err: err: ERR
out: out: out: OUT

또는 구성 가능한 "필터"를 사용하십시오.

# usage: louterrx out_filter err_filter cmd [args ...]
louterrx(){
   oflt=$1; eflt=$2; shift; shift
   eval "{ \"\$@\" 2>&1 >&3 | $eflt >&2; } 3>&1 | $oflt"
}

louterrx "sed 's/^/out: /'" "sed 's/^/err: /'" cmd

이것의 단점은 종료 상태가 cmd숨겨진다는 것입니다(파이프의 오른쪽 면과 마찬가지로). 이 문제를 해결하려면 서브셸 및 set -o pipefail( bash, zsh, 등에서는 ksh지원되지만 아직은 지원되지 않음 dash)을 사용할 수 있습니다.

louterr()(
   set -o pipefail
   { "$@" 2>&1 >&3 | sed 's/^/err: /' >&2; } 3>&1 | sed 's/^/out: /'
)
cmd(){ echo ERR >&2; echo OUT; return 13; }
louterr cmd; echo $?
out: OUT
err: ERR
13

이 경우에는 zsh/bash-ism을 사용 하지 않는 것이 좋습니다 > >(...). 이는 확장 및 리디렉션이 다른 쉘에서 수행되는 순서와 관련된 특이 사항에 의존하기 때문입니다(그리고 다른 많은 문제가 발생할 수 있음).특이한 점그리고곤충). 예를 들어,

ls no_such_file >/dev/null 2> >(cat)

cat출력(오류 메시지)은 in ls으로 리디렉션되지만 in 으로는 리디렉션되지 않습니다 ./dev/nullbashzsh

~가 이 cmd > >(...)형식을 사용해야 하는 유일한 경우 cmd는 현재 환경을 수정하는 것이 목적이고 서브쉘에서 실행될 수 없는 함수일 때입니다.

관련 정보