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?

adarshr
fonte
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
H.-Dirk Schmitt
fonte
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.

Stéphane Chazelas
fonte
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.

user2624583
fonte
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.

Kannan Mohan
fonte
IMHO esta é a maneira mais limpa de fazer isso, pelo menos em casos simples. Obrigado.
ACK_stoverflow