Eu tenho um script que será executado interativamente por usuários não técnicos. O script grava atualizações de status em STDOUT para que o usuário possa ter certeza de que o script está funcionando bem.
Desejo STDOUT e STDERR redirecionados para o terminal (para que o usuário possa ver se o script está funcionando e também se houve um problema). Também quero que os dois fluxos sejam redirecionados para um arquivo de log.
Eu vi um monte de soluções na rede. Alguns não funcionam e outros são terrivelmente complicados. Desenvolvi uma solução viável (que entrarei como uma resposta), mas é confusa.
A solução perfeita seria uma única linha de código que pudesse ser incorporada no início de qualquer script que envie ambos os fluxos para o terminal e um arquivo de log.
EDIT: Redirecionando STDERR para STDOUT e canalizando o resultado para trabalhos em T, mas depende dos usuários se lembrarem de redirecionar e canalizar a saída. Quero que o registro seja à prova de idiotas e automático (é por isso que gostaria de poder incorporar a solução ao próprio script).
Respostas:
Use "tee" para redirecionar para um arquivo e a tela. Dependendo do shell que você usa, você primeiro deve redirecionar stderr para stdout usando
ou
No csh, existe um comando embutido chamado "script" que irá capturar tudo que vai para a tela para um arquivo. Você o inicia digitando "script", fazendo tudo o que deseja capturar e, em seguida, pressiona control-D para fechar o arquivo de script. Não sei de um equivalente para sh / bash / ksh.
Além disso, como você indicou que esses são seus próprios scripts sh que podem ser modificados, você pode fazer o redirecionamento internamente circundando todo o script com colchetes ou colchetes, como
fonte
2>&1 | tee -a filename
não estava salvando stderr no arquivo do meu script, mas funcionou bem quando copiei o comando e colei no terminal! O truque do suporte funciona bem, no entanto.Quase meia década depois ...
Acredito que essa seja a "solução perfeita" buscada pelo OP.
Aqui está um liner que você pode adicionar ao topo de seu script Bash:
Aqui está um pequeno script demonstrando seu uso:
(Nota: Isto só funciona com Bash ele vai. Não trabalho com / bin / sh.)
Adaptado daqui ; o original não capturou, pelo que posso dizer, STDERR no arquivo de log. Corrigido com uma nota daqui .
fonte
O padrão
Isso redireciona stdout e stderr separadamente e envia cópias separadas de stdout e stderr para o chamador (que pode ser o seu terminal).
No zsh, ele não irá para a próxima instrução até que o
tee
s tenha terminado.No bash, você pode descobrir que as poucas linhas finais da saída aparecem após qualquer instrução que vier a seguir.
Em ambos os casos, os bits certos vão para os lugares certos.
Explicação
Aqui está um script (armazenado em ./example):
Aqui está uma sessão:
Funciona assim:
tee
processos são iniciados, seus stdins são atribuídos a descritores de arquivo. Como eles estão incluídos em substituições de processo , os caminhos para esses descritores de arquivo são substituídos no comando de chamada, então agora parece algo assim:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
é executado, gravando stdout no primeiro descritor de arquivo e stderr no segundo.No caso do bash, assim que
the_cmd
terminar, a seguinte instrução acontecerá imediatamente (se o seu terminal for o chamador, você verá seu prompt aparecer).No caso zsh, uma vez
the_cmd
concluído, o shell espera que ambos ostee
processos terminem antes de prosseguir. Mais sobre isso aqui .O primeiro
tee
processo, que está lendo dothe_cmd
stdout de, grava uma cópia desse stdout de volta para o chamador porque é isso quetee
faz. Suas saídas não são redirecionadas, então elas voltam para o chamador inalteradasO segundo
tee
processo éstdout
redirecionado para ostderr
responsável pela chamada (o que é bom, porque o stdin está lendo dothe_cmd
stderr de). Portanto, quando ele grava em seu stdout, esses bits vão para o stderr do chamador.Isso mantém o stderr separado do stdout tanto nos arquivos quanto na saída do comando.
Se o primeiro tee escrever algum erro, eles aparecerão tanto no arquivo stderr quanto no stderr do comando, se o segundo tee escrever qualquer erro, eles só aparecerão no stderr do terminal.
fonte
tee
está disponível no sistema em questão.) O erro que recebo é "O processo não pode acessar o arquivo porque ele está sendo usado por outro processo."o para redirecionar stderr para stdout acrescente isto ao seu comando:
2>&1
Para enviar para o terminal e fazer login no arquivo, você deve usartee
Ambos juntos ficariam assim:
EDIT: Para embutir em seu script, você faria o mesmo. Então seu roteiro
acabaria como
fonte
EDIT: Vejo que descarrilhei e acabei respondendo a uma pergunta diferente daquela feita. A resposta à verdadeira questão está na base da resposta de Paul Tomblin. (Se você quiser aprimorar essa solução para redirecionar stdout e stderr separadamente por algum motivo, pode usar a técnica que descrevo aqui.)
Tenho procurado uma resposta que preserve a distinção entre stdout e stderr. Infelizmente, todas as respostas dadas até agora que preservam essa distinção são propensas a raça: elas correm o risco de os programas receberem informações incompletas, como indiquei nos comentários.
Acho que finalmente encontrei uma resposta que preserva a distinção, não é propensa a raça e também não é terrivelmente complicada.
Primeiro bloco de construção: para trocar stdout e stderr:
Segundo bloco de construção: se quiséssemos filtrar (por exemplo, tee) apenas stderr, poderíamos fazer isso trocando stdout & stderr, filtrando e depois trocando de volta:
Agora o resto é fácil: podemos adicionar um filtro stdout, no início:
ou no final:
Para me convencer de que ambos os comandos acima funcionam, usei o seguinte:
O resultado é:
e meu prompt volta imediatamente após o "
teed stderr: to stderr
", conforme esperado.Nota de rodapé sobre zsh :
A solução acima funciona em bash (e talvez em alguns outros shells, não tenho certeza), mas não funciona em zsh. Existem duas razões pelas quais ele falha no zsh:
2>&3-
não é compreendida por zsh; que tem que ser reescrito como2>&3 3>&-
Então, por exemplo, minha segunda solução deve ser reescrita para zsh as
{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(que funciona em bash também, mas é terrivelmente prolixo).Por outro lado, você pode tirar proveito do misterioso teeing implícito embutido de zsh para obter uma solução muito mais curta para zsh, que não executa tee de forma alguma:
(Eu não teria adivinhado pelos documentos, descobri que
>&1
e2>&2
são as coisas que acionam a tacada implícita de zsh; descobri isso por tentativa e erro.)fonte
my_function
) na filtragem stdout / stderr aninhada? Sim,{ { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
mas parece estranho criar um arquivo como indicador de falha ...Use o
script
comando em seu script (script man 1)Crie um shellscript wrapper (2 linhas) que configura o script () e, em seguida, chama exit.
Parte 1: wrap.sh
Parte 2: realscript.sh
Resultado:
fonte
Use o programa tee e dup stderr para stdout.
fonte
Criei um script chamado "RunScript.sh". O conteúdo deste script é:
Eu chamo assim:
Isso funciona, mas requer que os scripts do aplicativo sejam executados por meio de um script externo. É um pouco confuso.
fonte
Um ano depois, aqui está um antigo script bash para registrar qualquer coisa. Por exemplo,
teelog make ...
registra um nome de registro gerado (e veja o truque para registrarmake
s aninhados também.)fonte