.xprofile
.NET을 사용할 때 stdout과 stderr을 별도로 기록 하고 싶습니다 logger
. Bash에서는 다음과 같이 보일 것이라고 생각합니다.
exec 1> >(logger --priority user.notice --tag $(basename $0)) \
2> >(logger --priority user.error --tag $(basename $0))
POSIX에서 이 작업을 어떻게 수행합니까? /bin/sh
호환 가능한 방법은?
답변1
POSIX 명령/프로세스 대체
_log()( x=0
while [ -e "${TMPDIR:=/tmp}/$$.$((x+=1))" ]
do continue; done &&
mkfifo -- "$TMPDIR/$$.$x" &&
printf %s\\n "$TMPDIR/$$.$x" || exit
exec >&- >/dev/null
{ rm -- "$TMPDIR/$$.$x"
logger --priority user."$1" --tag "${0##*/}"
} <"$TMPDIR/$$.$x" &
) <&- </dev/null
다음과 같이 사용할 수 있어야 합니다.
exec >"$(_log notice)" 2>"$(_log error)"
다음 명령을 사용하는 버전은 다음과 같습니다 mktemp
.
_log()( p=
mkfifo "${p:=$(mktemp -u)}" &&
printf %s "$p" &&
exec <&- >&- <>/dev/null >&0 &&
{ rm "$p"
logger --priority user."$1" --tag "${0##*/}"
} <"$p" &
)
mktemp
... 파일 이름을 선택할 수 있다는 점을 제외하면 거의 동일한 작업을 수행합니다 . 이것은 작동합니다.프로세스 교체결코 마술적이지는 않으며, 다음과 같은 방식으로 작동합니다.명령 대체. 확장명을 그 안에서 실행되는 명령의 값으로 바꾸는 대신명령 대체하다,프로세스 교체이를 출력을 찾을 수 있는 파일 시스템 링크의 이름으로 바꾸십시오.
POSIX 쉘은 이에 대한 직접적인 결과를 제공하지 않지만 이를 시뮬레이션하는 것은 매우 간단합니다. 당신이 해야 할 일은 파일을 만들고, 명령 대체를 통해 그 이름을 표준으로 인쇄한 다음, 파일에 출력될 동일한 배경에서 명령을 실행하는 것뿐입니다. 이제 해당 확장의 값으로 리디렉션할 수 있습니다.정확히프로세스 교체에서 했던 것과 같습니다. 따라서 POSIX 셸은 필요한 모든 도구를 확실히 제공합니다. 필요한 것은 자신에게 맞는 방식으로 도구를 사용하는 것뿐입니다.
위의 두 버전 모두 사용하기 전에 생성/사용하는 파이프의 파일 시스템 링크를 파괴합니다. 즉, 나중에 정리할 필요가 없으며 더 중요한 것은 해당 스트림을 원래 열었던 프로세스에서만 사용할 수 있다는 것입니다. 따라서 해당 파일 시스템 링크를 로깅 활동을 스누핑/하이재킹하는 수단으로 사용할 수 없습니다. 파일 시스템 링크를 파일 시스템에 남겨두면 잠재적인 보안 취약점이 발생합니다.
또 다른 방법은 포장하는 것입니다. 스크립트 내에서 수행할 수 있습니다.
x=${x##*[!0-9]*}
_log(){
logger --priority user."$1" --tag "${0##*/}"
} 2>/dev/null >&2
cd ../"$PPID.$x" 2>/dev/null &&
trap 'rm -rf -- "${TMPDIR:-/tmp}/$PPID.$x"' 0 ||
{ until cd -- "${TMPDIR:=/tmp}/$$.$x"
do mkdir -- "$TMPDIR/$$.$((x+=1))"
done &&
x=$x "$0" "$@" | _log notice
exit
} 2>&1 | _log error
이렇게 하면 기본적으로 스크립트가 자신을 호출할 수 있고(아직 호출되지 않은 경우) 시작할 임시 작업 디렉터리를 제공할 수 있습니다.
답변2
POSIX에 해당하는 것은 없습니다. Fork가 아닌 Perform Redirection만 사용할 수 있습니다 exec
. 파이프에는 포크가 필요하고 쉘은 하위 프로세스가 완료될 때까지 기다립니다.
한 가지 해결책은 모든 코드를 함수에 넣는 것입니다.
all_my_code () {
…
}
{ all_my_code |
logger --priority user.notice --tag "$(basename "$0")"; } 2>&1 |
logger --priority user.error --tag "$(basename "$0")"
(이것은 또한 로거의 stdout 인스턴스에 있는 모든 오류를 stderr 인스턴스에 기록합니다. 더 많은 파일 설명자를 섞으면 이를 방지할 수 있습니다.)
logger
프로세스가 계속 실행되는 동안 상위 셸을 종료하려면 이를 호출 &
끝에 넣으세요 logger
.
{ all_my_code |
logger --priority user.notice --tag "$(basename "$0")" & } 2>&1 |
logger --priority user.error --tag "$(basename "$0")" &
또는 명명된 파이프를 사용할 수 있습니다.
pipe_dir=$(mktemp -d)
mkfifo "$pipe_dir/out" "$pipe_dir/err"
<"$pipe_dir/out" logger --priority user.notice --tag "$(basename "$0")" &
<"$pipe_dir/err" logger --priority user.error --tag "$(basename "$0")" &
exec >"$pipe_dir/out" 2>"$pipe_dir/err"
…
rm -r "$pipe_dir"