Copie stdout e stderr para um arquivo de log e deixe-os no console dentro do próprio script


11

Usando o bash, como copio stderr e stdout para um arquivo de log e também os deixo exibidos no console?

Eu gostaria de fazer isso dentro do próprio script usando um exec.

Eu tentei com

exec &>> log.out

echo "This is stdout"
echo "This is stderr" >&2

Mas o acima exposto não imprime nada no console. Como posso conseguir isso no bash?


Há uma resposta altamente votada para uma pergunta semelhante no StackOverflow que responde a essa pergunta completamente. stackoverflow.com/a/692407/208257
Dan Burton

Respostas:


10

Você está procurando tee.

Veja man teepara detalhes.

Para combiná-lo exec, é necessário usar a substituição de processo . (Veja man bashpara detalhes.)

exec &> >(tee  log.out)
echo "This is stdout"
echo "This is stderr" >&2

Eu olhei para isso. Fazer exec 2>&1 | tee -a log.outapenas impressões no console, nada no arquivo de log. Talvez eu não esteja usando a sintaxe correta?
22413 adarshr

@adarshr Por favor, verifique seu código. Escrevi teeem combinação com a substituição do processo .
H.-Dirk Schmitt

1
Isso mescla stdout e stderr e pode ter o mesmo tipo de efeito colateral mencionado na minha resposta.
Stéphane Chazelas

@StephaneChazelas Veja o código de exemplo na pergunta - essa era a intenção do OP.
H.-Dirk Schmitt

O que quero dizer com mesclagem é que agora os erros do script vão para o stdout. Portanto, se alguém o fizer the-script | wc -l, isso também contará as linhas de erro e você não verá os erros.
Stéphane Chazelas

5

Você pode fazer:

: > log # empty log file if necessary
{ { {

  ...the script

} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log >&2 3>&-
} 3>&1
exit "${PIPESTATUS[0]}"

Você também pode escrever como:

: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)

...the script

Mas como o bash não aguarda os processos iniciados com >(...), isso tem o efeito desagradável de, às vezes, gerar algo para o terminal após o retorno do comando, que pode ter efeitos ainda mais desagradáveis ​​(como descartar silenciosamente essa saída) se o atributo "tostop" do terminal está ligado.

De qualquer forma, criando stdoutum canal nas duas soluções e como dois comandos emitem independentemente as mensagens de saída e erro, isso afetará o buffer de saída e a ordem em que as mensagens de saída e erro serão exibidas.


Eu acho que o seu segundo tee está faltando um redirecionamento para o stderr. Deveria ser:tee -a log >&2 3>&-
richvdh 4/14/14

5

Eu sei que este é um post antigo, mas por que não fazer isso?

echo "hi" >> log.txt #stdout -> log
echo "hi" | tee -a log.txt #stdout -> log & stdout
echo "hi" &>> log.txt #stdout & stderr -> log
echo "hi" |& tee -a log.txt #stdout & stderr -> log & stdout

E, claro, se você quiser o stdout, poderá imprimir regularmente.

Você pode fazer isso com qualquer combinação de fluxos que desejar, apenas usando esses dois comandos básicos.

Sei que vim para cá e não obtive uma resposta fácil de entender / implementar, espero que isso ajude alguém que esteja com dificuldades.

A propósito, para noobs por aí como o meu eu anterior, tudo o que o teecomando faz é gerar a entrada stdin para o stdout e os arquivos especificados como argumentos subsequentes. -asignifica acréscimo, para que você não sobrescreva o arquivo a cada uso do comando. Se você tiver mais perguntas, considero este um recurso muito útil para aprender rapidamente o bash.


1
Obrigado por contribuir. Eu, por outro lado, ter esquecido totalmente o que eu estava tentando fazer :-)
adarshr

1
+1 por fornecer vários exemplos de camisetas agradáveis ​​e simples.
Tim

2

Mais uma maneira de fazer isso é usar redirecionamentos nas funções.

#!/bin/bash

function1 () {
    echo 'STDOUT from function 1'
    echo 'STDERR from function 1' >&2
}

function2 () {
    echo 'STDOUT from function 2'
    echo 'STDERR from function 2' >&2
}


function3 () {
    echo 'STDOUT from function 3'
    echo 'STDERR from function 3' >&2
}

main() {
    function1
    function2
    function3
}

main 2>&1 |tee log.txt

Aqui temos uma mainfunção que chama todas as outras funções. Agora redirecionando STDOUTe STDERRde mainfunção para tee.


IMHO esta é a maneira mais limpa de fazer isso, pelo menos em casos simples. Obrigado.
ACK_stoverflow
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.