2012-03-09 14 views
5

ACTUALIZACIÓN: aunque no estoy resolviendo el problema original presentado con respecto a mis esfuerzos de tuberías, he resuelto mi problema simplificándolo enormemente, y simplemente abandonando las tuberías por completo. Aquí hay una secuencia de comandos de prueba de concepto que genera, en paralelo, mientras lee solo una vez del disco, las sumas de comprobación CRC32, MD5, SHA1, SHA224, SHA256, SHA384 y SHA512, y las devuelve como un objeto JSON (va a usar la salida en PHP y Ruby). Es crudo y sin comprobación de errores, pero funciona:Posible condición de carrera con salida de múltiples destinatarios que llegan fuera de secuencia en una tubería con nombre en un script de BASH

#!/bin/bash 

checksums="`tee <"$1" \ 
     >(cfv -C -q -t sfv -f - - | tail -n 1 | sed -e 's/^.* \([a-fA-F0-9]\{8\}\)$/"crc32":"\1"/') \ 
     >(md5sum - | sed -e 's/^\([a-fA-F0-9]\{32\}\) .*$/"md5":"\1"/') \ 
     >(sha1sum - | sed -e 's/^\([a-fA-F0-9]\{40\}\) .*$/"sha1":"\1"/') \ 
     >(sha224sum - | sed -e 's/^\([a-fA-F0-9]\{56\}\) .*$/"sha224":"\1"/') \ 
     >(sha256sum - | sed -e 's/^\([a-fA-F0-9]\{64\}\) .*$/"sha256":"\1"/') \ 
     >(sha384sum - | sed -e 's/^\([a-fA-F0-9]\{96\}\) .*$/"sha384":"\1"/') \ 
     >(sha512sum - | sed -e 's/^\([a-fA-F0-9]\{128\}\) .*$/"sha512":"\1"/') \ 
     >/dev/null`\ 
" 

json="{" 

for checksum in $checksums; do json="$json$checksum,"; done 

echo "${json:0: -1}}" 

la pregunta original:

estoy un poco de miedo a esta pregunta, ya que me dieron tantos golpes en mi frase de búsqueda que después de aplicar el conocimiento cosechado de Using named pipes with bash - Problem with data loss, y leyendo otras 20 páginas, todavía estoy un poco parado con esto.

Por lo tanto, para continuar, estoy haciendo una secuencia de comandos simple para permitirme al mismo tiempo crear sumas de comprobación CRC32, MD5 y SHA1 en un archivo mientras solo lo leo desde el disco una vez. Estoy usando cfv para ese propósito.

Originalmente, acabo de hackear un script simple que escribía cat'ted el archivo en el tee con tres comandos cfv escribiendo en tres archivos separados bajo/tmp /, y luego intentaba conectarlos a stdout luego, pero terminaba con salida vacía a menos que hice que mi script durmiera por un segundo antes de tratar de leer los archivos. Pensando que era raro, asumí que era un imbécil en mi guión, así que traté de hacer un enfoque diferente al hacer que los trabajadores cfv salieran a una tubería con nombre. Hasta ahora, este es mi guión, después de tener técnicas aplicadas desde el enlace antes mencionado:

!/bin/bash 

# Bail out if argument isn't a file: 
[ ! -f "$1" ] && echo "'$1' is not a file!" && exit 1 

# Choose a name for a pipe to stuff with CFV output: 
pipe="/tmp/pipe.chksms" 

# Don't leave an orphaned pipe on exiting or being terminated: 
trap "rm -f $pipe; exit" EXIT TERM 

# Create the pipe (except if it already exists (e.g. SIGKILL'ed b4)): 
[ -p "$pipe" ] || mkfifo $pipe 

# Start a background process that reads from the pipe and echoes what it 
# receives to stdout (notice the pipe is attached last, at done): 
while true; do 
     while read line; do 
       [ "$line" = "EOP" ] && echo "quitting now" && exit 0 
       echo "$line" 
     done 
done <$pipe 3>$pipe & # This 3> business is to make sure there's always 
         # at least one producer attached to the pipe (the 
         # consumer loop itself) until we're done. 

# This sort of works without "hacks", but tail errors out when the pipe is 
# killed, naturally, and script seems to "hang" until I press enter after, 
# which I believe is actually EOF to tail, so it's no solution anyway: 
#tail -f $pipe & 

tee <"$1" >(cfv -C -t sfv -f - - >$pipe) >(cfv -C -t sha1 -f - - >$pipe) >(cfv -C -t md5 -f - - >$pipe) >/dev/null 

#sleep 1s 
echo "EOP" >$pipe 
exit 

Así, ejecutado tal y como está, me sale esta salida:

[email protected]:~/tisso$ ./multisfv file 
: : : quitting now 
- : Broken pipe (CF) 
close failed in file object destructor: 
sys.excepthook is missing 
lost sys.stderr 
- : Broken pipe (CF) 
close failed in file object destructor: 
sys.excepthook is missing 
lost sys.stderr 
- : Broken pipe (CF) 
[email protected]:~/tisso$ close failed in file object destructor: 
sys.excepthook is missing 
lost sys.stderr 

Pero, con los 1s sueño comentadas , que la salida esperada conseguir,

[email protected]:~/tisso$ ./multisfv file 
3bc1b5ff125e03fb35491e7d67014a3e * 
-: 1 files, 1 OK. 0.013 seconds, 79311.7K/s 
5e3bb0e3ec410a8d8e14fef1a6daababfc48c7ce * 
-: 1 files, 1 OK. 0.016 seconds, 62455.0K/s 
; Generated by cfv v1.18.3 on 2012-03-09 at 23:45.23 
; 
2a0feb38 
-: 1 files, 1 OK. 0.051 seconds, 20012.9K/s 
quitting now 

Esto me extraña, ya que me supongo que no sería tee de salida hasta después de cada destinatario CFV se bifurca a los datos ha salido, y por lo tanto el eco "EOP" declaración sería ex hasta que todos los substreams de cfv hayan terminado, lo que significaría que habrían escrito su salida en mi pipe nombrada ... Y luego se ejecutaría la instrucción echo.

Como el comportamiento es el mismo sin tuberías, solo usando archivos temporales de salida, estoy pensando que esta debe ser una condición de carrera que tenga que ver con la forma en que tee envía datos a sus destinatarios. Probé con un simple comando de "espera", pero por supuesto espero que mi proceso bash child, el ciclo while, termine, así que solo tengo un proceso de suspensión.

¿Alguna idea?

TIA, Daniel :)

+1

Espero que haya un código fuente disponible para estas sumas de comprobación. Qué tal combinarlos en 1 programa y escribir 3 valores que procesa en el archivo de suma de comprobación apropiado. Tengo que creer que Perl probablemente tiene módulos para esto, que una vez más, podrían planear juntos para hacer solo 1 pase en el archivo. (Solo pensando en el lado de la caja en esto, YRMV). ¡Buena suerte! – shellter

+1

¿Ayudaría esto? 'parallel - group 'cfv -C -t sfv -f {} -; cfv -C -t sha1 -f {} -; cfv -C -t md5 -f {} -;' ::: file' – potong

+0

@shelter - Supongo que escribir mis propias rutinas siempre es mi retroceso, pero preferiría usar las herramientas ya disponibles tanto como sea posible. – DanielSmedegaardBuus

Respuesta

2

camiseta va a salir una vez que se escribe el último bit de la entrada a la última tubería de salida y la cierra (es decir, las tuberías sin nombre creado por bash, no su FIFO, también conocido como " tubería nombrada "). No tiene que esperar a que finalicen los procesos que leen las tuberías; de hecho, no tiene idea de que incluso está escribiendo en tuberías. Como las tuberías tienen búferes, es bastante probable que el tee termine de escribir antes de que los procesos en el otro extremo hayan terminado de leerse. Entonces el script escribirá 'EOP' en el fifo, causando que el ciclo de lectura termine. Eso cerrará el único lector de la fifo, y todos los procesos cfv obtendrán SIGPIPE cuando intenten escribir en stdout.

La pregunta obvia que debe hacerse aquí es por qué no solo ejecuta tres (o N) procesos independientes leyendo el archivo y calculando diferentes resúmenes. Si "el archivo" en realidad se generó sobre la marcha o se descargó desde un sitio remoto, o algún otro proceso lento, podría tener sentido hacer las cosas de la manera en que intentas hacerlo, pero si el archivo está presente en local disco, es bastante probable que solo se produzca un solo acceso al disco; los sumarizadores rezagados leerán el archivo de la memoria caché del búfer. Si eso es todo lo que necesita, GNU paralelo debería funcionar bien, o simplemente puede iniciar los procesos en bash (con &) y luego esperarlos. YMMV, pero creo que cualquiera de estas soluciones requerirá menos recursos que la configuración de todas esas tuberías y la simulación de la memoria caché del búfer en el territorio del usuario con tee.

Por cierto, si desea serializar la salida de múltiples procesos, puede utilizar la utilidad flock. Solo usar un fifo no es suficiente; no hay garantía de que los procesos que escriben en la fifo escriban líneas enteras de forma atómica y si supiera que lo hicieron, no necesitaría la fifo.

Cuestiones relacionadas