Eu preciso fazer upload da saída do script atual, então adicionei um trap
e set -ex
, por exemplo
#!/bin/bash
exec &> /tmp/error.log
trap 'cat /tmp/error.log; curl http://127.0.0.1/error.php?hostname=$(hostname) -F file=@/tmp/error.log' EXIT
set -ex
wtfwtf
Quando executo, sempre recebo esse erro, e o script PHP não recebeu o arquivo inteiro
%> cat /tmp/error.log
1.sh: line 6: wtfwtf: command not found
cat: /tmp/error.log: input file is output file
Até agora, a única solução é copiar o error.log para um novo arquivo e carregá-lo, por exemplo
#!/bin/bash
exec &> /tmp/error.log
trap 'cp /tmp/error.log 123; curl http://127.0.0.1/error.php?hostname=$(hostname) -F file=@123' EXIT
set -ex
wtfwtf
Existe alguma maneira melhor de fazer isso?
Com o
exec
, você está redirecionando toda a saída do script para um arquivo de log específico.Em seu trap, você deseja exibir o conteúdo do arquivo de log usando
cat
. Como toda a saída também é redirecionada para esse arquivo, o GNUcat
percebe que seu arquivo de entrada e fluxo de saída padrão (que é herdado do shell) são a mesma coisa e se recusa a realizar sua tarefa.O BSD
cat
não faz a mesma verificação que o GNUcat
, que, se o script não for interrompido, resulta em um arquivo de log infinitamente grande com as mesmas poucas linhas repetidas várias vezes.Uma solução alternativa é salvar o descritor de arquivo de saída padrão original, fazer o redirecionamento como antes e restabelecê-lo no trap.
Isso faz uma cópia do descritor de arquivo 1 (como fd 3) antes de redirecioná-lo para o arquivo de log. Na armadilha, movemos essa cópia de volta para fd 1 e fazemos a saída.
Observe que o fluxo de erro padrão, no trap, neste exemplo, ainda está conectado ao arquivo de log. Portanto, se
curl
gerar uma mensagem de diagnóstico, ela será salva no arquivo de log em vez de ser exibida no terminal (ou onde quer que o fluxo de erro padrão original tenha sido conectado).Tendo em conta o comentário de Stéphane Chazelas :
Seu ponto é que o arquivo de log é apenas para mensagens de diagnóstico de qualquer maneira, então faz mais sentido enviar o arquivo de log para o fluxo de erro padrão original.
Ele também aponta que é perigoso usar um nome de arquivo fixo em um diretório gravável como
/tmp
. Isso ocorre porque nenhuma verificação é colocada no script para garantir que esse arquivo ainda não exista (alguém ou algum malware pode ter criado um/tmp/error.log
link simbólico para/etc/passwd
você~/.bashrc
, por exemplo). Sua solução para isso é usar um arquivo de log persistente dedicado para o script em/var/log
vez disso (o arquivo é persistente, mas o conteúdo será limpo ao executar o script).Uma variação disso seria usar
mktemp
para criar um nome de arquivo exclusivo em$TMPDIR
(e, em seguida, remover esse arquivo naEXIT
armadilha, a menos que falhe,curl
caso em querm
não seria executado, poisset -e
está em vigor):Seu segundo exemplo funciona, mas apenas porque você não está usando
cat
no arquivo de log, não por copiá-lo.Pequeno detalhe: URLs na linha de comando provavelmente devem estar sempre entre aspas duplas, pois tendem a conter caracteres que o shell pode interpretar como especiais (por exemplo
?
).