데이터를 파이프하고 데이터의 "도착"을 표시하는 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/null
bash
zsh
~가 이 cmd > >(...)
형식을 사용해야 하는 유일한 경우 cmd
는 현재 환경을 수정하는 것이 목적이고 서브쉘에서 실행될 수 없는 함수일 때입니다.