2012-03-29 11 views
15

Ich versuche derzeit, ein Skript zu erhalten, um Ausgaben von anderen gestarteten Befehlen korrekt in eine Protokolldatei zu schreiben. Das Skript schreibt seine eigenen Nachrichten in die Protokolldatei mit echo und es gibt eine Methode, die ich die Zeilen von dem anderen Programm pipen kann.Ist echo atomar beim Schreiben einzelner Zeilen

Das Hauptproblem ist, dass das Programm, das die Ausgabe erzeugt, im Hintergrund gestartet wird, so dass meine Funktion, die den Lesevorgang ausführt, in die Protokolldatei geschrieben wird. Könnte das ein Problem sein? Echo schreibt immer nur eine einzige Zeile, daher sollte es nicht schwer sein, die Atomarität sicherzustellen. Jedoch habe ich in Google gesucht und ich habe keinen Weg gefunden, um sicherzustellen, dass es tatsächlich atomar ist.

Hier ist das aktuelle Skript:

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 

Wie Sie sehen können, kann das Skript schreiben, sowohl auf als auch besitzen ist, als wegen umgeleitet ausgegeben. Könnte dies zu Havok in der Datei führen?

+0

Ich denke nicht, Bash ist das richtige Werkzeug für diesen Job. Ich würde etwas mächtigeres empfehlen (Perl, Python, Ruby ..) – Daenyth

+4

Übrigens, wenn Sie eine * Menge * von Daten in dieses Protokoll gehen dann können Sie feststellen, dass das ständige Öffnen und Schließen der Protokolldatei eine schlechte Idee ist. Sie können eine Datei dauerhaft mit 'exec 3 >> $ {LOG_FILE}' öffnen und dann jederzeit mit 'echo whatever> & 3' dorthin schreiben. Sie können die Datei mit 'exec 3> & -' schließen, aber das passiert, wenn das Skript trotzdem beendet wird. Das einzige Problem ist, dass Sie für jede geöffnete Datei manuell eine Nummer auswählen müssen und 0, 1 und 2 bereits vergeben sind. – ams

Antwort

26

echo nur ein einfacher Wrapper um write (dies ist eine Vereinfachung; siehe unten für die blutigen Details), um festzustellen, ob Echo atomar ist, ist es sinnvoll, nachschlagen zu schreiben. Vom single UNIX specification:

Atomic/Nicht-Atom: Ein Schreib ist atomar, wenn die ganze Menge in einem Vorgang geschrieben wird nicht mit Daten aus anderen Prozessen verschachtelt. Dies ist nützlich, wenn mehrere Writer Daten an einen einzelnen Reader senden. Anwendungen müssen wissen, wie groß eine Schreibanforderung voraussichtlich atomar ausgeführt werden kann. Dieses Maximum wird {PIPE_BUF} genannt. Dieses Volumen von IEEE Std 1003.1-2001 sagt nicht, ob Schreibanforderungen für mehr als {PIPE_BUF} Bytes atomar sind, erfordert jedoch, dass Schreibvorgänge von {PIPE_BUF} oder weniger Bytes atomar sein müssen.

Sie können PIPE_BUF auf Ihrem System mit einem einfachen C-Programm überprüfen. Wenn Sie nur eine einzige Ausgabezeile drucken, die nicht lächerlich lang ist, sollte sie atomar sein.

Hier ist ein einfaches Programm, um den Wert von PIPE_BUF zu überprüfen:

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

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

    return 0; 
} 

Unter Mac OS X, das mir 512 (die minimum allowed value für PIPE_BUF) gibt. Unter Linux bekomme ich 4096. Wenn Ihre Zeilen also ziemlich lang sind, überprüfen Sie es auf dem betreffenden System.

bearbeiten hinzufügen: Ich entschied mich zu überprüfen the implementation of echo in Bash, um zu bestätigen, dass es atomar gedruckt wird. Es stellt sich heraus, echo verwendet putchar oder printf je nachdem, ob Sie die Option -e verwenden. Dies sind gepufferte stdio-Operationen, was bedeutet, dass sie einen Puffer füllen und ihn nur dann schreiben, wenn ein Zeilenumbruch erreicht ist (im liniengepufferten Modus), der Puffer gefüllt ist (im blockgepufferten Modus) oder Sie explizit spülen der Ausgang mit fflush. Standardmäßig befindet sich ein Stream im Zeilenpuffermodus, wenn es sich um ein interaktives Terminal handelt, und im Blockpuffermodus, wenn es sich um eine andere Datei handelt. Bash legt niemals den Pufferungstyp fest, daher sollte er für die Protokolldatei standardmäßig den Puffermodus blockieren. Um dann end of the echo builtin Bash calls fflush den Ausgangsstrom zu spülen. Daher wird die Ausgabe immer am Ende von echo geleert, kann aber früher gelöscht werden, wenn sie nicht in den Puffer passt.

Die Größe des verwendeten Puffers kann BUFSIZ sein, obwohl es anders sein kann; BUFSIZ ist die Standardgröße, wenn Sie den Puffer explizit mit setbuf festlegen, aber es gibt keinen portablen Weg, um die tatsächliche Größe Ihres Puffers zu bestimmen. Es gibt auch keine tragbaren Richtlinien für das, was BUFSIZ ist, aber wenn ich es unter Mac OS X und Linux getestet habe, war es doppelt so groß wie PIPE_BUF.

Was bedeutet das alles? Da die Ausgabe von echo vollständig gepuffert ist, ruft sie die write nicht wirklich auf, bis der Puffer voll ist oder fflush aufgerufen wird. An diesem Punkt sollte die Ausgabe geschrieben werden, und die oben erwähnte Atomaritätsgarantie sollte gelten. Wenn die Stdout-Puffergröße größer als PIPE_BUF ist, dann ist PIPE_BUF die kleinste atomare Einheit, die ausgeschrieben werden kann. Wenn PIPE_BUF größer ist als die Puffergröße von stdout, schreibt der Stream den Puffer, wenn der Puffer voll ist.

So, echo ist nur garantiert, um Sequenzen kürzer als die kleinere von PIPE_BUF und die Größe des Stdout-Puffer, die am wahrscheinlichsten ist BUFSIZ atomar schreiben. Bei den meisten Systemen ist BUFSIZ größer als PIPE_BUF.

tl; dr: echo werden Zeilen atomar ausgeben, solange diese Zeilen kurz genug sind. Auf modernen Systemen sind Sie wahrscheinlich bis zu 512 Byte sicher, aber es ist nicht möglich, das Limit portabel zu bestimmen.

+4

Hier ist eine Tabelle der beobachteten 'PIPE_BUF'-Werte auf gängigen Unix-Systemen: http://ar.to/notes/posix#pipe-buf –

0

Es gibt keine unfreiwillige Dateiverriegelung, aber der >> Operator ist sicher, der> Operator ist unsicher. Deine Praxis ist also sicher.