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.