2012-03-29 10 views
15

Actualmente estoy tratando de obtener un script para escribir la salida de otros comandos iniciados correctamente en un archivo de registro. El script escribirá sus propios mensajes en el archivo de registro utilizando echo y hay un método para canalizar las líneas del otro programa.Es echo atomic al escribir líneas simples

El problema principal es que el programa que produce la salida se inicia en segundo plano, por lo que mi función que hace la lectura puede escribir con concurencia en el archivo de registro. ¿Podría esto ser un problema? Eco siempre solo escribe una sola línea, por lo que no debería ser difícil garantizar la atomicidad. Sin embargo, he buscado en google y no he encontrado manera de asegurarme de que sea atómico.

Aquí está el script actual:

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 

Como se puede ver, el guión podría escribir tanto por sí mismo, así como debido a la salida redirigida. ¿Podría esto causar havok en el archivo?

+0

No creo que bash sea la herramienta adecuada para este trabajo. Recomendaría algo más poderoso (perl, python, ruby ​​...) – Daenyth

+4

Por cierto, si tiene un * lote * de datos en este registro, entonces puede encontrar que abrir y cerrar continuamente el archivo de registro es una mala idea. Puede abrir permanentemente un archivo con 'exec 3 >> $ {LOG_FILE}' y luego escribir en él cuando lo desee con 'echo whatever> & 3'. Puede cerrar el archivo con 'exec 3> & -', pero eso ocurrirá cuando el script salga de todos modos. El único problema es que debe elegir manualmente un número para cada archivo que abre, y 0, 1 y 2 ya están ocupados. – ams

Respuesta

26

echo simplemente una envoltura simple alrededor de write (esto es una simplificación, ver la edición más abajo para los detalles sangrientos), por lo que para determinar si el eco es atómico, es útil buscar escribir. Desde el single UNIX specification:

Atómica/no atómica: Una escritura es atómica si toda la cantidad escrita en una sola operación no está intercalada con los datos de cualquier otro proceso. Esto es útil cuando hay escritores múltiples que envían datos a un solo lector. Las aplicaciones necesitan saber qué tan grande se puede esperar que se realice una solicitud de escritura atómicamente. Este máximo se llama {PIPE_BUF}. Este volumen de IEEE Std 1003.1-2001 no dice si las solicitudes de escritura para más de {PIPE_BUF} bytes son atómicas, sino que requiere que las escrituras de {PIPE_BUF} o menos bytes sean atómicas.

Puede verificar PIPE_BUF en su sistema con un simple programa en C. Si solo imprime una sola línea de salida, que no es ridículamente larga, debería ser atómica.

Aquí es un programa sencillo para comprobar el valor de PIPE_BUF:

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

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

    return 0; 
} 

En Mac OS X, que me da 512 (el minimum allowed value para PIPE_BUF). En Linux, obtengo 4096. Por lo tanto, si sus líneas son bastante largas, asegúrese de verificarlas en el sistema en cuestión.

edit para agregar: Decidí consultar the implementation of echo en Bash, para confirmar que se imprimirá atómicamente. Resulta que echo usa putchar o printf dependiendo de si usa la opción -e. Estas son operaciones stdio almacenadas en búfer, lo que significa que llenan un búfer, y realmente lo escriben solo cuando se llega a una nueva línea (en modo buffer de línea), el búfer se llena (en modo búfer de bloque), o se vacía explícitamente la salida con fflush. Por defecto, una secuencia estará en modo de línea protegida si es una terminal interactiva, y bloqueará el modo almacenado si es cualquier otro archivo. Bash nunca establece el tipo de almacenamiento en búfer, por lo que para su archivo de registro, debe establecerse de forma predeterminada para bloquear el modo de almacenamiento en búfer. En ese momento end of the echo builtin, Bash calls fflush para enjuagar el flujo de salida. Por lo tanto, la salida siempre se purgará al final de echo, pero puede enjuagarse antes si no cabe en el búfer.

El tamaño del búfer utilizado puede ser BUFSIZ, aunque puede ser diferente; BUFSIZ es el tamaño predeterminado si configura el búfer de forma explícita utilizando setbuf, pero no hay forma portátil de determinar el tamaño real de su búfer. Tampoco hay pautas portátiles para lo que es BUFSIZ, pero cuando lo probé en Mac OS X y Linux, era dos veces el tamaño de PIPE_BUF.

¿Qué significa todo esto? Como la salida de echo está almacenada en el búfer, no llamará realmente al write hasta que se llene el búfer o se llame al fflush. En ese punto, la salida debe escribirse, y debe aplicarse la garantía de atomicidad que mencioné anteriormente. Si el tamaño del búfer estándar es mayor que PIPE_BUF, entonces PIPE_BUF será la unidad atómica más pequeña que se pueda escribir. Si PIPE_BUF es más grande que el tamaño del búfer estándar, el flujo escribirá el búfer cuando el búfer se llene.

Así, echo sólo se garantiza para escribir atómicamente secuencias más cortas que el más pequeño de PIPE_BUF y el tamaño del búfer de salida estándar, que es más probable BUFSIZ. En la mayoría de los sistemas, BUFSIZ es más grande que PIPE_BUF.

tl; dr: echo saldrán líneas atómicamente, siempre que esas líneas sean lo suficientemente cortas. En los sistemas modernos, probablemente tenga una seguridad de hasta 512 bytes, pero no es posible determinar el límite de forma portátil.

+4

Aquí hay una tabla de valores 'PIPE_BUF' observados en sistemas comunes de Unix: http://ar.to/notes/posix#pipe-buf –

0

No hay bloqueo de archivo involuntario, pero el operador >> es seguro, el operador> no es seguro. Entonces tu práctica es segura de hacer.

Cuestiones relacionadas