Eu sei como redirecionar stdout para um arquivo:
exec > foo.log
echo test
isso colocará o 'teste' no arquivo foo.log.
Agora eu quero redirecionar a saída para o arquivo de log E mantê-lo no stdout
isto é, pode ser feito trivialmente de fora do script:
script | tee foo.log
mas eu quero declarar isso dentro do próprio script
eu tentei
exec | tee foo.log
mas não funcionou.
Respostas:
Note que
bash
não é issosh
. Se você chamar o scriptsh myscript.sh
, receberá um erro ao longo das linhas desyntax error near unexpected token '>'
.Se você estiver trabalhando com traps de sinal, convém usar a
tee -i
opção para evitar interrupções na saída se ocorrer um sinal. (Obrigado a JamesThomasMoon1979 pelo comentário.)As ferramentas que alteram sua saída dependendo de gravar em um tubo ou terminal (
ls
usando cores e saída em colunas, por exemplo) detectarão a construção acima como significando que elas serão enviadas para um tubo.Existem opções para aplicar a coloração / colunização (por exemplo
ls -C --color=always
). Observe que isso resultará na gravação dos códigos de cores no arquivo de log, tornando-o menos legível.fonte
tee
não deve armazenar em buffer sua saída. Se ele faz buffer na maioria dos sistemas, está quebrado na maioria dos sistemas. Esse é um problema dastee
implementações, não da minha solução.exec
é muito poderoso, mas também muito envolvido. Você pode "fazer backup" do stdout atual para um outro editor de arquivos e recuperá-lo posteriormente. Google "bash exec tutorial", há muitas coisas avançadas por aí.exec
é documentado para não iniciar novos processos,>(tee ...)
é um padrão chamado de substituição da tubulação / processo, eo&
no redirecionamento, obviamente, nada a ver com backgrounding tem ... :-)?-i
paratee
. Caso contrário, as interrupções de sinal (interrupções) interromperão o stdout no script principal. Por exemplo, se você tiver umtrap 'echo foo' EXIT
e depois pressionarctrl+c
, não verá " foo ". Então, eu modificaria a resposta paraexec &> >(tee -ia file)
.A resposta aceita não preserva STDERR como um descritor de arquivo separado. Que significa
não será enviado
bar
para o terminal, apenas para o arquivo de log eproduzirá ambos
foo
ebar
para o terminal. Claramente, esse não é o comportamento que um usuário normal provavelmente espera. Isso pode ser corrigido usando dois processos tee separados, ambos anexando ao mesmo arquivo de log:(Observe que o acima não inicialmente trunca o arquivo de log - se você deseja esse comportamento, adicione
para o topo do script.)
A especificação POSIX.1-2008 de
tee(1)
exige que a saída seja sem buffer, ou seja, nem com buffer de linha; portanto, neste caso, é possível que STDOUT e STDERR possam terminar na mesma linha defoo.log
; no entanto, isso também pode acontecer no terminal, portanto o arquivo de log será um reflexo fiel do que pode ser visto no terminal, se não um espelho exato dele. Se você deseja que as linhas STDOUT sejam separadas de maneira limpa das linhas STDERR, considere o uso de dois arquivos de log, possivelmente com prefixos de carimbo de data em cada linha para permitir a remontagem cronológica posteriormente.fonte
exec > >(tee -a $LOG)
trap "kill -9 $! 2>/dev/null" EXIT
exec 2> >(tee -a $LOG >&2)
trap "kill -9 $! 2>/dev/null" EXIT
-i
paratee
. Caso contrário, as interrupções de sinal (interrupções) interromperão o stdout no script. Por exemplo, se vocêtrap 'echo foo' EXIT
pressionar e, em seguidactrl+c
, não verá " foo ". Então, eu modificaria a resposta paraexec > >(tee -ia foo.log)
.. log
ou. log foo.log
: sam.nipl.net/sh/log sam.nipl.net/sh/log-aSTDOUT
exibidas primeiro como um lote e, em seguida, as mensagens serãoSTDERR
exibidas. Eles não são intercalados como normalmente esperado.Solução para shells busybox, macOS bash e não bash
A resposta aceita é certamente a melhor escolha para o bash. Estou trabalhando em um ambiente Busybox sem acesso ao bash e ele não entende a
exec > >(tee log.txt)
sintaxe. Ele também não funcionaexec >$PIPE
corretamente, tentando criar um arquivo comum com o mesmo nome que o pipe nomeado, que falha e trava.Espero que isso seja útil para outra pessoa que não tem festança.
Além disso, para qualquer pessoa que use um pipe nomeado, é seguro
rm $PIPE
, porque isso desassocia o pipe do VFS, mas os processos que o utilizam ainda mantêm uma contagem de referência até que sejam concluídos.Observe que o uso de $ * não é necessariamente seguro.
fonte
Dentro do seu arquivo de script, coloque todos os comandos entre parênteses, assim:
fonte
{}
)Maneira fácil de criar um script bash para o syslog. A saída do script está disponível através
/var/log/syslog
e através do stderr. O syslog adicionará metadados úteis, incluindo registros de data e hora.Adicione esta linha na parte superior:
Como alternativa, envie o log para um arquivo separado:
Isso requer
moreutils
(para ots
comando, que adiciona timestamps).fonte
Usando a resposta aceita, meu script continuava retornando excepcionalmente cedo (logo após 'exec>> (tee ...)') deixando o restante do meu script em execução em segundo plano. Como não consegui fazer com que a solução funcionasse, encontrei outra solução / solução para o problema:
Isso faz com que a saída do script vá do processo, passando pelo canal até o processo secundário de 'tee', que registra tudo no disco e no stdout original do script.
Observe que 'exec &>' redireciona stdout e stderr; podemos redirecioná-los separadamente, se quisermos, ou mudar para 'exec>', se quisermos apenas stdout.
Mesmo que o tubo seja removido do sistema de arquivos no início do script, ele continuará funcionando até o término do processo. Nós simplesmente não podemos fazer referência a ele usando o nome do arquivo após a linha rm.
fonte
$logfile
partetee < ${logfile}.pipe $logfile &
. Especificamente, tentei alterar isso para capturar linhas de log de comando expandidas completas (deset -x
) para arquivo, enquanto mostrava apenas linhas sem levar '+' no stdout, alterando para(tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &
mas recebi uma mensagem de erro referente$logfile
. Você pode explicar atee
linha um pouco mais detalhadamente?O Bash 4 possui um
coproc
comando que estabelece um pipe nomeado para um comando e permite que você se comunique através dele.fonte
Não posso dizer que estou confortável com qualquer uma das soluções baseadas em exec. Eu prefiro usar tee diretamente, então eu faço o script se chamar com tee quando solicitado:
Isso permite que você faça isso:
Você pode personalizar isso, por exemplo, tornar tee = false o padrão, em vez disso, fazer com que o TEE retenha o arquivo de log etc. Acho que essa solução é semelhante à do jbarlow, mas mais simples, talvez a minha tenha limitações que ainda não encontrei.
fonte
Nenhuma dessas é uma solução perfeita, mas aqui estão algumas coisas que você pode tentar:
ou
O segundo deixaria um arquivo de pipe parado se algo desse errado com o seu script, o que pode ou não ser um problema (ou seja, talvez você possa fazer
rm
isso no shell pai posteriormente).fonte
tee
- eu editei. Como eu disse, nenhuma das duas é uma solução perfeita, mas os processos em segundo plano serão eliminados quando o shell pai terminar, para que você não precise se preocupar com a necessidade de consumir recursos para sempre.