POSIX 셸에서 모든 출력을`logger '로 보내는 방법은 무엇입니까?


10

.xprofile사용하여 표준 출력과 표준 오류를 별도로 기록하고 싶습니다 logger. Bash에서는 다음과 같이 보일 것이라고 생각합니다.

exec 1> >(logger --priority user.notice --tag $(basename $0)) \
     2> >(logger --priority user.error --tag $(basename $0))

어떻게에서 그렇게 할 것 POSIX /bin/sh 호환 방법?

답변:


10

POSIX에 해당하는 것이 없습니다. 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"

이는 전체 스크립트에 대해 syslog에 두 개의 항목 (out & err)으로 나타납니다. 문제의 예에는 명령 당 하나 또는 두 개의 항목이 있습니다.
DarkHeart

@DarkHeart 귀하의 의견을 이해하지 못합니다. 질문의 코드와 두 솔루션 모두 동일한 수의 인스턴스를 실행 logger하고 동일한 입력을 각 인스턴스에 전달합니다.
Gilles 'SO- 악한 중지'

미안, 내 실수
DarkHeart

1
이것은 더 단순 해 보이 므로 명명 된 파이프 솔루션으로 갈 것입니다.
l0b0

9

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 셸은 물론 필요한 모든 도구를 제공합니다. 필요한 것은 도구를 자신에게 맞는 방식으로 사용하기 만하면됩니다.

위의 두 버전은 파일 시스템을 사용하기 전에 작성 / 사용하는 파이프에 대한 파일 시스템 링크를 제거합니다. 즉, 사실 후에 정리가 필요하지 않으며, 더 중요한 것은 스트림을 처음 여는 프로세스에서만 스트림을 사용할 수 있으므로 파일 시스템 링크를 로깅 활동을 스누핑 / 강탈하는 수단으로 사용할 수 없습니다. 파일 시스템에 fs-links를 두는 것은 잠재적 인 보안 허점입니다.


다른 방법은 포장하는 것입니다. 스크립트 내에서 수행 할 수 있습니다.

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

기본적으로 스크립트가 아직 호출되지 않은 경우 자체 호출하여 작업 디렉토리를 부팅 할 수있게합니다.


1
완료 , 감사합니다!
l0b0

@ l0b0-나는 이런 종류의 다른 종류 를 업데이트하려고합니다 ... 더 일반적인 목적이며 마침내 i / o 설명자 / 파일과 관련된 일부 옵션을 처리 할 수있었습니다.
mikeserv

@ l0b0- mktemp다른 비표준 명령 을 사용하려는 경우 다음 과 같습니다 _log()( p=; mkfifo "${p:=$(mktemp -u)}"; printf %s "$p"; exec <&- >&- <>/dev/null >&0; { rm "$p"; logger --priority user."$1" --tag "${0##*/}"; } <"$p" &).. 나는 당신의 것을 제외하고는 모든 방법으로 완전히 휴대용 명령 언어를 사용하여 그것을 썼습니다. 또한 basename매우 유용한 유틸리티는 아닙니다. "${0##*/}"더 강력하고 빠릅니다.
mikeserv

현대적인 플랫폼에서만 작동하기 때문에 변경했습니다. 주요 문제는 그했다 .xprofile 함께 실행되는 sh.
l0b0

1
@Wildcard-잠깐만 질문의 첫 번째 부분을 정렬하려고 시도하지만 두 번째에 대한 대답은 '예' zsh입니다. 첫 부분은 : { this; can\'t; happen; before; } < this. 도움이 되나요?
mikeserv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.