Forza lo svuotamento dell'output in un file mentre lo script bash è ancora in esecuzione


90

Ho un piccolo script, che viene chiamato quotidianamente da crontab usando il seguente comando:

/homedir/MyScript &> some_log.log

Il problema con questo metodo è che some_log.log viene creato solo al termine di MyScript. Vorrei scaricare l'output del programma nel file mentre è in esecuzione in modo da poter fare cose come

tail -f some_log.log

e tieni traccia dei progressi, ecc.


Avremo bisogno di una descrizione, o se possibile del codice, di ciò che fa esattamente il tuo piccolo script ...
ChristopheD

7
Per sbloccare gli script Python puoi usare "python -u". Per sbloccare gli script Perl, vedere la risposta di Greg Hewgill di seguito. E così via ...
Eloici

Se puoi modificare lo script, di solito puoi svuotare il buffer di output esplicitamente all'interno di uno script, ad esempio in python con sys.stdout.flush().
drevicko

Risposte:


28

bash stesso non scriverà mai alcun output nel tuo file di log. Invece, i comandi che invoca come parte dello script scriveranno individualmente l'output e scaricheranno ogni volta che ne hanno voglia. Quindi la tua domanda è davvero come forzare lo scarico dei comandi all'interno dello script bash, e questo dipende da cosa sono.


25
Davvero non capisco questa risposta.
Alfonso Santiago,

3
Per un'idea migliore del perché l'output standard si comporta in questo modo, controlla stackoverflow.com/a/13933741/282728 . Una versione breve: per impostazione predefinita, se reindirizzato a un file, lo stdout è completamente bufferizzato; viene scritto in un file solo dopo un flush. Stderr non lo è: è scritto dopo ogni "\ n". Una soluzione è utilizzare il comando "script" consigliato da user3258569 di seguito, per fare in modo che stdout venga scaricato dopo ogni fine di riga.
Alex

2
Affermando l'ovvio, e dieci anni dopo, ma questo è un commento, non una risposta, e non dovrebbe essere la risposta accettata.
RealHandy

87

Ho trovato una soluzione a questo qui . Usando l'esempio dell'OP che fondamentalmente corri

stdbuf -oL /homedir/MyScript &> some_log.log

e quindi il buffer viene scaricato dopo ogni riga di output. Spesso combino questo con nohupl'esecuzione di lavori lunghi su una macchina remota.

stdbuf -oL nohup /homedir/MyScript &> some_log.log

In questo modo il tuo processo non verrà annullato quando ti disconnetti.


1
Potresti aggiungere un collegamento ad alcuni documenti per stdbuf? Sulla base di questo commento sembra che non sia disponibile su alcune distribuzioni. Potresti chiarire?
Finanzia la causa di Monica il

1
stdbuf -o regola il buffering dello stdout. Le altre opzioni sono -i e -e per stdin e stderr. L imposta il buffer di linea. Si potrebbe anche specificare la dimensione del buffer o 0 per nessun buffering.
Seppo Enarvi

5
quel collegamento non è più disponibile.
john-jones

2
@NicHartley: stdbuffa parte di GNU coreutils, la documentazione può essere trovata su gnu.org
Thor

Nel caso in cui aiuti qualcuno, usa export -f my_function e poi stdbuf -oL bash -c "my_function -args"se hai bisogno di eseguire una funzione invece di uno script
Anonimo

29
script -c <PROGRAM> -f OUTPUT.txt

La chiave è -f. Citazione dalla sceneggiatura dell'uomo:

-f, --flush
     Flush output after each write.  This is nice for telecooperation: one person
     does 'mkfifo foo; script -f foo', and another can supervise real-time what is
     being done using 'cat foo'.

Esegui in background:

nohup script -c <PROGRAM> -f OUTPUT.txt

Wow! Una soluzione che funziona busybox! (il mio guscio si blocca dopo, ma qualunque cosa)
Victor Sergienko

9

È possibile utilizzare teeper scrivere sul file senza bisogno di svuotamento.

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null

2
Questo bufferizza ancora l'output, almeno nel mio ambiente Ubuntu 18.04. I contenuti alla fine vengono scritti nel file in entrambi i modi, ma penso che l'OP stia chiedendo un metodo in cui possano monitorare l'avanzamento in modo più accurato prima che il file abbia finito di scrivere, e questo metodo non lo consente più del reindirizzamento dell'output fa.
mltsy

3

Questa non è una funzione di bash, poiché tutto ciò che la shell fa è aprire il file in questione e quindi passare il descrittore di file come output standard dello script. Quello che devi fare è assicurarti che l'output venga cancellato dallo script più frequentemente di quanto lo sei attualmente.

In Perl, ad esempio, ciò potrebbe essere ottenuto impostando:

$| = 1;

Vedi perlvar per maggiori informazioni su questo.


2

Il buffering dell'output dipende da come /homedir/MyScriptviene implementato il programma . Se trovi che l'output viene bufferizzato, devi forzarlo nella tua implementazione. Ad esempio, usa sys.stdout.flush () se è un programma Python o usa fflush (stdout) se è un programma C.


2

Questo aiuterebbe?

tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq 

Verranno immediatamente visualizzate voci univoche da access.log utilizzando l' utilità stdbuf .


L'unico problema è che stdbuf sembra essere una vecchia utility che non è disponibile sulle nuove distribuzioni.
Ondra Žižka

..non nella mia casella occupata :(
Campa

In realtà ho a stdbufdisposizione in Ubuntu al momento, non sono sicuro di dove l'ho preso.
Ondra Žižka

Ho stdbuf in Centos 7.5
Max

1
In Ubuntu 18.04, stdbuffa parte di coreutilus(trovato con apt-file search /usr/bin/stdbuf).
Rmano

1

Come appena individuato qui il problema è che devi aspettare che i programmi che esegui dal tuo script finiscano il loro lavoro.
Se nel tuo script esegui il programma in background puoi provare qualcosa di più.

In generale una chiamata a syncprima di uscire consente di svuotare i buffer del file system e può aiutare un po '.

Se nello script avvii alcuni programmi in formato background ( &), puoi aspettare che finiscano prima di uscire dallo script. Per avere un'idea di come può funzionare puoi vedere di seguito

#!/bin/bash
#... some stuffs ...
program_1 &          # here you start a program 1 in background
PID_PROGRAM_1=${!}   # here you remember its PID
#... some other stuffs ... 
program_2 &          # here you start a program 2 in background
wait ${!}            # You wait it finish not really useful here
#... some other stuffs ... 
daemon_1 &           # We will not wait it will finish
program_3 &          # here you start a program 1 in background
PID_PROGRAM_3=${!}   # here you remember its PID
#... last other stuffs ... 
sync
wait $PID_PROGRAM_1
wait $PID_PROGRAM_3  # program 2 is just ended
# ...

Poiché waitfunziona sia con i lavori che con i PIDnumeri, una soluzione pigra dovrebbe essere quella di mettere alla fine dello script

for job in `jobs -p`
do
   wait $job 
done

Più difficile è la situazione se esegui qualcosa che esegue qualcos'altro in background perché devi cercare e attendere (se è il caso) la fine di tutto il processo figlio : ad esempio se esegui un demone probabilmente non è così aspettare che finisca :-).

Nota:

  • wait $ {!} significa "aspetta fino al completamento dell'ultimo processo in background" dove $!è il PID dell'ultimo processo in background. Quindi per metterewait ${!} subito dopo program_2 &equivale a eseguire direttamente program_2senza inviarlo in background con&

  • Con l'aiuto di wait:

    Syntax    
        wait [n ...]
    Key  
        n A process ID or a job specification
    

1

Grazie @user3258569, lo script è forse l'unica cosa che funziona busybox!

Dopo, però, il guscio si stava congelando. Cercando la causa, ho trovato questi grandi avvisi rossi "non utilizzare in shell non interattive" nella pagina di manuale dello script :

scriptè progettato principalmente per sessioni terminali interattive. Quando stdin non è un terminale (ad esempio :) echo foo | script, la sessione può bloccarsi, perché la shell interattiva all'interno della sessione di script manca EOF e scriptnon ha idea di quando chiudere la sessione. Vedere la sezione NOTE per ulteriori informazioni.

Vero. script -c "make_hay" -f /dev/null | grep "needle"stava congelando il guscio per me.

In contrasto con l'avvertimento, ho pensato che echo "make_hay" | scriptpasserà un EOF, quindi ho provato

echo "make_hay; exit" | script -f /dev/null | grep 'needle'

e ha funzionato!

Notare gli avvertimenti nella pagina man. Questo potrebbe non funzionare per te.


0

un'alternativa a stdbuf è awk '{print} END {fflush()}' che vorrei che ci fosse un builtin bash per farlo. Normalmente non dovrebbe essere necessario, ma con le versioni precedenti potrebbero esserci bug di sincronizzazione di bash sui descrittori di file.


-2

Non so se funzionerebbe, ma per quanto riguarda la chiamata sync?


1
syncè un'operazione del filesystem di basso livello e non è correlata all'output bufferizzato a livello dell'applicazione.
Greg Hewgill,

2
syncscrive eventuali buffer sporchi del filesystem nella memoria fisica, se necessario. Questo è interno al sistema operativo; le applicazioni in esecuzione sul sistema operativo vedono sempre una vista coerente del filesystem indipendentemente dal fatto che i blocchi del disco siano stati scritti o meno nella memoria fisica. Per la domanda originale, l'applicazione (script) sta probabilmente memorizzando l'output in un buffer interno all'applicazione, e il sistema operativo non saprà nemmeno (ancora) che l'output è effettivamente destinato a essere scritto su stdout. Quindi un'ipotetica operazione di tipo "sincronizzazione" non sarebbe in grado di "raggiungere" lo script ed estrarre i dati.
Greg Hewgill,

-2

Ho avuto questo problema con un processo in background in Mac OS X utilizzando il StartupItems. Ecco come lo risolvo:

Se lo sudo ps auxfaccio posso vedere che mytoolviene lanciato.

Ho scoperto che (a causa del buffering) quando Mac OS X si spegne mytoolnon trasferisce mai l'output al sedcomando. Tuttavia, se eseguo sudo killall mytool, mytooltrasferisce l'output al sedcomando. Quindi, ho aggiunto un stopcaso a quello StartupItemsche viene eseguito quando Mac OS X si spegne:

start)
    if [ -x /sw/sbin/mytool ]; then
      # run the daemon
      ConsoleMessage "Starting mytool"
      (mytool | sed .... >> myfile.txt) & 
    fi
    ;;
stop)
    ConsoleMessage "Killing mytool"
    killall mytool
    ;;

Questa non è davvero una buona risposta Freeman poiché è molto specifica per il tuo ambiente. L'OP vuole monitorare l'output non ucciderlo.
Grey

-3

bene che piaccia o no, è così che funziona il reindirizzamento.

Nel tuo caso l'output (ovvero il tuo script è terminato) del tuo script è stato reindirizzato a quel file.

Quello che vuoi fare è aggiungere quei reindirizzamenti nel tuo script.

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.