Forçar o script bash a usar tee sem canalizar na linha de comando

20

Eu tenho um script bash com muita saída e gostaria de mudar esse script (não criar um novo) para copiar toda a saída em um arquivo, exatamente como aconteceria se eu estivesse passando o tee pelo tee.

Eu tenho um arquivo script.sh :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

e gostaria de fazer uma cópia do STDOUT em um arquivo script.log sem precisar digitar ./script.sh | tee script.logtodas as vezes.

Obrigado Tom

Tom
fonte
Sinto muito por não ter dito isso antes, mas o script é bastante longo, e é por isso que estou procurando uma solução simples. Anexando | tee para cada linha é um pouco demais.
24411 Tom

Respostas:

15

Eu não conseguia fazer o one-liner muito simples de Dennis funcionar, então aqui está um método muito mais complicado. Eu tentaria o primeiro dele.

Como mencionado, você pode usar exec para redirecionar o erro padrão e o padrão para o script inteiro. Assim:
exec > $LOGFILE 2>&1 Isso produzirá todos os stderr e stdout para $ LOGFILE.

Agora, como você deseja que isso seja exibido no console e também em um arquivo de log, você também precisará usar um pipe nomeado para o exec escrever e o tee para ler.
(O one-liner de Dennis tecnicamente também faz isso, embora obviamente de uma maneira diferente). O pipe em si é criado mkfifo $PIPEFILE. Então faça o seguinte.

# Comece escrevendo tee em um arquivo de log, mas retirando sua entrada do nosso pipe nomeado.
tee $ LOGFILE <$ PIPEFILE &

# capture o ID do processo do tee para o comando wait.
TEEPID = $!

# redireciona o restante do stderr e stdout para o nosso pipe nomeado.
exec> $ PIPEFILE 2> & 1

eco "Faça seus comandos aqui"
eco "Todos os seus padrões serão atendidos."
eco "O erro padrão deles também será>" & 2

# fecha os descritores de arquivo stderr e stdout.
exec 1> & - 2> & -

# Aguarde o tee terminar, pois agora a outra extremidade do tubo foi fechada.
aguarde $ TEEPID

Se você quiser ser completo, poderá criar e destruir o arquivo de pipe nomeado no início e no final do seu script.

Para constar, eu colhi a maior parte disso no post muito informativo de um cara aleatório: ( Versão arquivada )

Christopher Karel
fonte
Isso não parece funcionar no cygwin. :-(
docwhat
Este post faz um bom trabalho de educação. Muito obrigado.
Felipe Alvarez
27

Basta adicionar isso ao início do seu script:

exec > >(tee -ia script.log)

Isso anexará toda a saída enviada ao stdout ao arquivo script.log, deixando o conteúdo anterior no lugar. Se você deseja começar do zero sempre que o script for executado, adicione-o rm script.logimediatamente antes desse execcomando ou remova-o -ado teecomando.

A -iopção faz teecom que ignore os sinais de interrupção e pode permitir teecapturar um conjunto de saída mais completo.

Adicione esta linha para também capturar todos os erros (em um arquivo separado):

exec 2> >(tee -ia scripterr.out)

Os espaços entre os vários >símbolos são importantes.

Pausado até novo aviso.
fonte
Solução muito interessante.
23909 Dech
2
Essa é uma maneira realmente elegante de fazer isso, em comparação com toda a porcaria que publiquei. Mas quando eu crio um script com essas duas linhas, ele trava e nunca sai corretamente.
Christopher Karel
Qual versão do Bash?
Pausado até novo aviso.
GNU bash, versão 3.2.25 (1) como parte de uma instalação do RHEL5.3. Parece que esta é a versão mais recente dos repositórios oficiais.
23420 Christopher Karel
Eu apenas tentei com o cygwin no Bash 3.2.49 (23) -release e obtive erros de descritor de arquivo. No entanto, eu trabalho no lançamento do Bash 4.0.33 (1) no Ubuntu 9.10, então pode ser uma coisa do Bash 4.
Pausado até novo aviso.
6

Esta é a versão combinada da resposta postada por Dennis Williamson anteriormente. Anexa err e std no script.log na ordem correta.

Adicione esta linha ao início do seu script:

exec > >(tee -a script.log) 2>&1

Observe o espaço entre os >sinais. Funciona para mim no GNU bash, versão 3.2.25 (1) -release (x86_64-redhat-linux-gnu)

ttt
fonte
5

Suponho que outra abordagem seja algo assim:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 
Glenn Jackman
fonte
3

Se você deseja uma cópia da saída, pode usar teedesta maneira:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

No entanto, isso só registrará stdout no script.log. Se você deseja garantir que o stderr e o stdout sejam redirecionados, use:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

Você pode até torná-lo um pouco melhor com uma pequena função:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini
ℝaphink
fonte
Boa resposta. Se ele realmente se importa apenas com stdout, eu deixaria de 2>&1fora e daria um tapa.
DaveParillo 23/12/2009
É verdade, eu só incluí o stderr porque a resposta anterior de Igor se preocupou em redirecioná-lo e achei uma boa ideia.
23409
1

Você usaria isso:

script logfile.txt
your script or command here

Isso gera tudo para esse log, tudo, além de exibi-lo na tela. Se você deseja a saída COMPLETA, é assim que se faz.

E você pode reproduzir o script novamente se tiver preguiça.

Phishy
fonte
Pode ser bom notar que scripté um pacote, por exemplo. sudo apt-get install scriptpara instalá-lo em distribuições com sabor Debian , adicionalmente script -ac 'command opts' ~/Documents/some_log.scriptpode ser revisado dentro de um terminal através de algo como strings ~/Documents/some_log.script | more; stringsser uma maneira simples de pré-higienizar algumas das coisas script injetadas para permitir métodos mais sofisticados de reprodução / exibição. Caso contrário, uma resposta sólida @ Phhishy, ​​porque scripttambém preserva coisas interativas.
S0AndS0 14/05/19
0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

Você pode ver mais sobre como isso funciona aqui: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/

Igor Zinov'yev
fonte
11
Adicionei um segundo à segunda linha na resposta de Igor, para que não apague o log escrito na primeira linha.
Doug Harris
11
Ele quer uma cópia, então ele precisa usar tee.
23409
@Raphink: Isso é :) direita
Tom