Como enviar toda a saída para o 'logger' no shell POSIX?

8

Gostaria de registrar a saída padrão e o erro padrão separadamente em .xprofile usando logger . No Bash, acho que seria algo assim:

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

Como eu faria isso de uma maneira compatível POSIX /bin/sh ?

    
por l0b0 27.07.2015 / 00:11

2 respostas

8

Não há equivalente POSIX. Você só pode executar um redirecionamento com exec , não um fork. Um pipe requer um fork e o shell aguarda o filho terminar.

Uma solução é colocar todo o seu código em uma função.

all_my_code () {
  …
}
{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")"; } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")"

(Isso também registra qualquer erro da instância stdout do logger para a instância stderr. Você pode evitar isso com mais embaralhamento de descritores de arquivo.)

Se você deseja que o shell pai seja encerrado, mesmo que os processos logger ainda estejam em execução, coloque & no final das invocações logger .

{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")" & } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")" &

Como alternativa, você pode usar pipes nomeados.

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"
    
por 27.07.2015 / 00:24
7

Comando POSIX / substituição de processo

_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

Você deve ser capaz de usar isso como:

exec >"$(_log notice)" 2>"$(_log error)"

Aqui está uma versão que faz uso do comando mktemp :

_log()( p=
    mkfifo "${p:=$(mktemp -u)}"    &&
    printf %s "$p"                 &&
    exec  <&- >&- <>/dev/null >&0  &&
    {   rm "$p"
        logger --priority user."$1" --tag "${0##*/}"
    }   <"$p" &
)

... que faz o mesmo, exceto pelo fato de permitir que mktemp selecione o nome do arquivo para você. Isso funciona porque a substituição de processos não é de forma alguma mágica e funciona de maneira muito semelhante à substituição de comandos . Em vez de substituir a expansão pelo valor do comando executado dentro dela, como substituição de comando , a substituição de processo substitui-a pelo nome de um link do sistema de arquivos onde a saída pode ser encontrada .

Embora o shell POSIX não forneça um corolário direto para tal coisa, a emulação é muito simples. Tudo o que você precisa fazer é criar um arquivo, imprimir seu nome no padrão de uma substituição de comando e, no segundo plano, executar seu comando, que será enviado para esse arquivo. Agora você pode apenas redirecionar para o valor dessa expansão - exatamente como faz com a substituição do processo. E assim, o shell POSIX fornece todas as ferramentas que você precisa, é claro - tudo o que é necessário é que você as coloque para usá-las da maneira que mais lhe convier.

Ambas as versões acima garantem que eles destruam o link do sistema de arquivos para os canais que eles criam / usam antes de usá-los. Isso significa que não há limpeza necessária após o fato e, mais importante, seus fluxos estão disponíveis apenas para os processos que inicialmente os abrem - e, portanto, seus links de sistema de arquivos não podem ser usados como um meio de rastrear / seqüestrar sua atividade de log. Deixar seus links fs no sistema de arquivos é uma possível falha de segurança.

Outra maneira é envolvê-lo. Isso pode ser feito dentro do script.

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

Isso basicamente permitiria que seu script chamasse a si mesmo, se ainda não tivesse, e obteria um diretório de trabalho em temp para inicializar.

    
por 27.07.2015 / 00:32