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


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 )


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.


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)


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 

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

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.


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/


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