2012-03-29 9 views
15

Attualmente sto cercando di ottenere uno script per scrivere l'output da altri comandi avviati correttamente in un file di registro. Lo script scriverà i propri messaggi nel file di registro usando echo e c'è un metodo con cui posso reindirizzare le linee dell'altro programma.È eco atomico quando si scrivono singole linee

Il problema principale è che il programma che produce l'output viene avviato in background, quindi la mia funzione che esegue la lettura potrebbe scrivere in modo coerente nel file di registro. Potrebbe essere un problema? Echo scrive sempre una sola riga, quindi non dovrebbe essere difficile garantire l'atomicità. Tuttavia ho cercato su google e non ho trovato alcun modo per assicurarmi che sia effettivamente atomico.

Ecco lo script corrente:

LOG_FILE=/path/to/logfile 

write_log() { 
    echo "$(date +%Y%m%d%H%M%S);$1" >> ${LOG_FILE} 
} 

write_output() { 
    while read data; do 
    write_log "Message from SUB process: [ $data ]" 
    done 
} 

write_log "Script started" 
# do some stuff 
call_complicated_program 2>&1 | write_output & 
SUB_PID=$! 
#do some more stuff 
write_log "Script exiting" 
wait $SUB_PID 

Come si può vedere, lo script potrebbe scrivere sia su di essa la propria così come a causa della produzione reindirizzato. Potrebbe questo causare havok nel file?

+0

Non credo che bash sia lo strumento giusto per questo lavoro. Suggerirei qualcosa di più potente (perl, python, ruby ​​..) – Daenyth

+4

Per inciso, se si dispone di un * lotto * di dati che vanno in questo registro, si potrebbe scoprire che aprire e chiudere continuamente il file di registro è una cattiva idea. Puoi aprire in modo permanente un file con 'exec 3 >> $ {LOG_FILE}' e poi scriverci ogni volta che vuoi con 'echo whatever> & 3'. Puoi chiudere il file con 'exec 3> & -', ma ciò avverrà quando lo script si chiude comunque. L'unico problema è che devi scegliere manualmente un numero per ogni file che apri e 0, 1 e 2 sono già stati presi. – ams

risposta

26

echo solo un semplice wrapper attorno a write (questa è una semplificazione, vedere la modifica in basso per i dettagli cruenti), quindi per determinare se echo è atomico, è utile cercare la scrittura. Dal single UNIX specification:

atomica/non-atomica: Una scrittura è atomico se l'intero importo scritto in una sola operazione non è intervallata con i dati provenienti da qualsiasi altro processo. Questo è utile quando ci sono più scrittori che inviano dati a un singolo lettore. Le applicazioni devono sapere quanto è grande che una richiesta di scrittura possa essere eseguita atomicamente. Questo valore massimo è chiamato {PIPE_BUF}. Questo volume di IEEE Std 1003.1-2001 non dice se le richieste di scrittura per più di {PIPE_BUF} byte sono atomiche, ma richiede che le scritture di {PIPE_BUF} o meno byte siano atomiche.

È possibile controllare PIPE_BUF sul proprio sistema con un semplice programma C. Se stai stampando una singola riga di output, non è ridicolmente lungo, dovrebbe essere atomico.

Ecco un semplice programma per controllare il valore di PIPE_BUF:

#include <limits.h> 
#include <stdio.h> 

int main(void) { 
    printf("%d\n", PIPE_BUF); 

    return 0; 
} 

In Mac OS X, che mi dà 512 (il minimum allowed value per PIPE_BUF). Su Linux, ottengo 4096. Quindi se le tue linee sono abbastanza lunghe, assicurati di controllarle sul sistema in questione.

modifica per aggiungere: Ho deciso di controllare the implementation of echo in Bash, per confermare che verrà stampato atomicamente. Risulta che echo utilizza putchar o printf a seconda se si utilizza l'opzione -e. Si tratta di operazioni stdio memorizzate nel buffer, il che significa che riempiono un buffer e in realtà lo scrivono solo quando viene raggiunta una nuova riga (in modalità buffer di riga), il buffer viene riempito (in modalità bufferizzato) o si scarica esplicitamente l'output con fflush. Per impostazione predefinita, un flusso sarà in modalità bufferizzata di linea se si tratta di un terminale interattivo e blocca la modalità bufferizzata se si tratta di un altro file. Bash non imposta mai il tipo di buffering, quindi per il tuo file di registro dovrebbe essere impostato in modo predefinito per bloccare la modalità di buffering. A quel punto, end of the echo builtin, Bash calls fflush per svuotare il flusso di output. Pertanto, l'output verrà sempre svuotato alla fine di echo, ma potrebbe essere svuotato in precedenza se non si adatta al buffer.

La dimensione del buffer utilizzato può essere BUFSIZ, sebbene possa essere diversa; BUFSIZ è la dimensione predefinita se si imposta il buffer in modo esplicito utilizzando setbuf, ma non esiste un modo portatile per determinare l'effettiva dimensione del buffer. Non ci sono anche linee guida portatili per ciò che è BUFSIZ, ma quando l'ho provato su Mac OS X e Linux, era il doppio di PIPE_BUF.

Che cosa significa tutto ciò? Poiché l'output di echo è tutto bufferizzato, in realtà non chiama lo write finché non viene riempito il buffer o viene chiamato fflush. A quel punto, l'output dovrebbe essere scritto e si dovrebbe applicare la garanzia di atomicità di cui sopra. Se la dimensione del buffer stdout è maggiore di PIPE_BUF, allora PIPE_BUF sarà la più piccola unità atomica che può essere scritta. Se PIPE_BUF è più grande della dimensione del buffer stdout, il flusso scriverà il buffer quando il buffer si riempie.

Quindi, echo è garantita solo scrivere atomicamente sequenze più corte rispetto alla più piccola del PIPE_BUF e la dimensione del buffer stdout, che è più probabile BUFSIZ. Sulla maggior parte dei sistemi, lo BUFSIZ è più grande di quello PIPE_BUF.

tl; dr: echo emetterà atomicamente le linee, a condizione che tali linee siano sufficientemente corte. Sui sistemi moderni, probabilmente sei sicuro fino a 512 byte, ma non è possibile determinarne il limite.

+4

Ecco una tabella dei valori osservati 'PIPE_BUF' sui sistemi Unix comuni: http://ar.to/notes/posix#pipe-buf –

0

Non esiste un blocco di file involontario, ma l'operatore >> è sicuro, l'operatore> non è sicuro. Quindi la tua pratica è sicura da fare.

Problemi correlati