Forza lo script bash per usare tee senza piping dalla riga di comando


20

Ho uno script bash con un sacco di output e vorrei cambiare quello script (non crearne uno nuovo) per copiare tutto l'output in un file, proprio come accadrebbe se lo stessi eseguendo il piping attraverso tee.

Ho un file script.sh :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

e vorrei fare una copia di STDOUT in un file script.log senza dover digitare ./script.sh | tee script.logogni volta.

Grazie Tom


Mi dispiace non averlo detto prima, ma la sceneggiatura è piuttosto lunga, motivo per cui sto cercando una soluzione semplice. Aggiunta | tee per ogni riga è un po 'troppo.
Tom,

Risposte:


15

Non riuscivo a far funzionare la linea molto semplice di Dennis, quindi ecco un metodo molto più complicato. Proverei per primo.

Come accennato, è possibile utilizzare exec per reindirizzare errore standard e standard out per l'intero script. In questo modo:
exec > $LOGFILE 2>&1 questo genererà tutti gli standard stderr e stdout su $ LOGFILE.

Ora, dal momento che vuoi che questo sia visualizzato sulla console e su un file di log, dovrai anche usare una pipe con nome per exec per scrivere e da cui leggere.
(Tecnicamente lo stesso one-liner di Dennis lo fa, anche se ovviamente in modo diverso) Viene creato il pipe stesso mkfifo $PIPEFILE. Quindi procedere come segue.

# Inizia a scrivere su un file di log, ma estraendo il suo input dalla nostra pipe denominata.
tee $ LOGFILE <$ PIPEFILE &

# capture ID processo tee per il comando wait.
TEEPID = $!

# reindirizza il resto dello stderr e dello stdout alla nostra pipa denominata.
exec> $ PIPEFILE 2> & 1

echo "Dai qui i tuoi comandi"
echo "Tutti i loro standard usciranno.
echo "Così sarà il loro errore standard"> & 2

# chiude i descrittori di file stderr e stdout.
exec 1> & - 2> & -

# Attendere il completamento del tee poiché ora che l'altra estremità del tubo è stata chiusa.
attendere $ TEEPID

Se vuoi essere accurato, puoi creare e distruggere il file pipe nominato all'inizio e alla fine dello script.

Per la cronaca, ho raccolto gran parte di questo dal post sul blog molto istruttivo di un ragazzo a caso: ( Versione archiviata )


Questo non sembra funzionare in Cygwin. :-(
docwhat

Questo post fa un buon lavoro di educazione. Grazie mille.
Felipe Alvarez,

27

Aggiungilo semplicemente all'inizio del tuo script:

exec > >(tee -ia script.log)

Ciò aggiungerà tutto l'output inviato a stdout al file script.log, lasciando in posizione i contenuti precedenti. Se vuoi ricominciare ogni volta che lo script viene eseguito, aggiungi rm script.logsubito prima di quel execcomando o rimuovi il -adal teecomando.

L' -iopzione consente teedi ignorare i segnali di interruzione e può consentire teedi acquisire un set di output più completo.

Aggiungi questa riga per rilevare anche tutti gli errori (in un file separato):

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

Gli spazi tra i >simboli multipli sono importanti.


Soluzione molto interessante
inkaphink,

2
Questo è un modo davvero elegante di farlo, rispetto a tutte le cazzate che ho pubblicato. Ma quando creo una sceneggiatura con quelle due righe, si blocca e non esce mai correttamente.
Christopher Karel,

Quale versione di Bash?
In pausa fino a nuovo avviso.

GNU bash, versione 3.2.25 (1) come parte di un'installazione RHEL5.3. Sembra che questa sia l'ultima versione dei repository ufficiali.
Christopher Karel,

L'ho appena provato sotto cygwin in Bash 3.2.49 (23) e ho riscontrato errori nel descrittore di file. Lavoro su Bash 4.0.33 (1) in Ubuntu 9.10, tuttavia, quindi potrebbe essere una cosa di Bash 4.
In pausa fino a nuovo avviso.

6

Questa è la versione combinata della risposta pubblicata da Dennis Williamson in precedenza. Aggiunge err e std a script.log nell'ordine giusto.

Aggiungi questa riga all'inizio del tuo script:

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

Nota lo spazio tra i >segni. Funziona per me su GNU bash, versione 3.2.25 (1) -release (x86_64-redhat-linux-gnu)


5

Suppongo che un altro approccio sia qualcosa del genere:

#!/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 si desidera una copia dell'output, è possibile utilizzare in teequesto modo:

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

Tuttavia, verrà registrato solo stdout su script.log. Se vuoi assicurarti che sia lo stderr sia lo stdout vengano reindirizzati, usa:

  #!/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

Potresti anche renderlo un po 'più bello con una piccola funzione:

  #!/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

Buona risposta. Se davvero si preoccupasse solo di stdout, lascerei semplicemente 2>&1fuori e lo tuberei per collegarti.
DaveParillo,

È vero, ho incluso solo stderr perché la risposta precedente di Igor voleva reindirizzarla e ho pensato che fosse una buona idea.
inkaphink,

1

Utilizzeresti solo questo:

script logfile.txt
your script or command here

Questo genera tutto su quel registro, tutto, oltre a visualizzarlo sullo schermo. Se vuoi l'output COMPLETO, ecco come farlo.

E puoi quindi riprodurre lo script se sei pigro.


Potrebbe essere utile notare che si scripttratta di un pacchetto, ad es. sudo apt-get install scriptper installarlo su distro aromatizzati Debian , script -ac 'command opts' ~/Documents/some_log.scriptpuò anche essere rivisto all'interno di un terminale tramite qualcosa di simile strings ~/Documents/some_log.script | more; stringsessere un modo semplice per pre-sanificare alcune delle cose che script inietta per consentire metodi più fantasiosi di re-play / view. Altrimenti una risposta solida @Phishy, ​​perché scriptconserva anche cose interattive.
S0AndS0

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

Puoi vedere di più su come funziona qui: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/


1
Ho aggiunto un secondo> alla seconda riga nella risposta di Igor in modo che non cancelli il registro scritto nella prima riga.
Doug Harris,

1
Vuole però una copia, quindi deve usarlo tee.
ℝaphink,

@Raphink: Esatto :)
Tom,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.