POSIX 쉘의 "로거"로 모든 출력을 보내는 방법은 무엇입니까?

POSIX 쉘의 "로거"로 모든 출력을 보내는 방법은 무엇입니까?

.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"

관련 정보