2012-03-09 7 views
5

AGGIORNAMENTO: Sebbene non risolvendo effettivamente il problema originale presentato per quanto riguarda i miei sforzi di piping, ho risolto il mio problema semplificandolo notevolmente e abbandonando del tutto i tubi. Ecco uno script proof-of-concept che genera, parallelamente durante la lettura di una sola volta dal disco, i checksum CRC32, MD5, SHA1, SHA224, SHA256, SHA384 e SHA512 e li restituisce come oggetto JSON (utilizzerà l'output in PHP e Ruby). E 'grezzo, senza controllo degli errori, ma funziona:Possibile condizione di competizione con uscita in pipe da più destinatari di t in arrivo fuori sequenza su una pipe denominata in uno script 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 domanda iniziale:

Sono un po' paura di fare questa domanda, come ho avuto tante visite per la mia frase di ricerca che dopo l'applicazione del conoscenza raccolta da Using named pipes with bash - Problem with data loss, e la lettura attraverso altre 20 pagine, sono ancora un po 'ferma con questo.

Quindi, per continuare comunque, sto facendo un semplice script per consentirmi di creare contemporaneamente checksum CRC32, MD5 e SHA1 su un file mentre lo leggo solo da disco una sola volta. Sto usando cfv per quello scopo.

Originariamente, ho appena hackerato un semplice script che ha scritto cat'ted il file in tee con tre comandi cfv che scrivevano su tre file separati in/tmp /, e poi ho provato a inviarli allo stdout in seguito, ma alla fine con output vuoto a meno che non abbia fatto dormire il mio script per un secondo prima di provare a leggere i file. Pensando che fosse strano, immaginavo di essere un idiota nel mio scripting, quindi ho provato a fare un approccio diverso facendo in modo che i worker di cfv producessero in una named pipe. Finora, questo è il mio script, dopo aver tecniche applicate da collegamento suindicato:

!/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 

Così, eseguito così com'è, ottengo questo output:

[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 

Ma, con i 1s sonno commentate , ho l'uscita ottenere previsto,

[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 

Questo mi lascia perplesso, come mi piacerebbe pensare che tee non sarebbe uscita fino a dopo ogni destinatario CFV si biforca dati è uscito, e quindi l'eco "EOP" affermazione sarebbe ex ecute fino a quando tutti i substream di cfv non sono terminati, il che significherebbe che avrebbero scritto il loro output sulla mia named pipe ... E quindi l'istruzione echo sarebbe stata eseguita.

Dato che il comportamento è lo stesso senza pipe, utilizzando solo i file temporanei di output, penso che questo debba essere una condizione di competizione che riguarda il modo in cui tee spinge i dati sui suoi destinatari? Ho provato un semplice comando "wait", ma ovviamente aspetterò che il mio processo bash child - the while loop - finisca, quindi ho appena un processo di sospensione.

Qualche idea?

TIA, Daniel :)

+1

Mi aspetto che ci sia il codice sorgente disponibile per questi checksum. Che ne dici di combinarli in 1 programma e di scrivere 3 valori da elaborare nel file di checksum appropriato. Devo credere che perl probabilmente ci sono dei moduli per questo, che, ancora una volta, potresti farti unire insieme per fare solo 1 passaggio sul file. (Sto solo pensando al lato della scatola su questo, YRMV). In bocca al lupo! – shellter

+1

Questo aiuto? 'parallel --group 'cfv -C -t sfv -f {} -; cfv -C -t sha1 -f {} -; cfv -C -t md5 -f {} -;' ::: file' – potong

+0

@shelter - Credo che scrivere le mie routine sia sempre la mia riserva, ma preferirei usare gli strumenti già disponibili per quanto possibile. – DanielSmedegaardBuus

risposta

2

tee uscirà una volta che scrive l'ultimo pezzo di input per l'ultimo tubo di uscita e lo chiude (vale a dire, i tubi senza nome create da bash, non il vostro FIFO, alias " named pipe "). Non ha bisogno di aspettare i processi che leggono i tubi per finire; in effetti, non ha idea che sia addirittura scritto su pipe. Dato che i tubi hanno dei buffer, è molto probabile che la terminazione t sia scritta prima che i processi all'altra estremità abbiano finito di leggere. Quindi lo script scriverà 'EOP' nel fifo, facendo terminare il ciclo di lettura. Ciò chiuderà l'unico lettore del fifo e tutti i processi cfv riceveranno SIGPIPE quando tenteranno di scrivere su stdout.

L'ovvia domanda da fare qui è perché non si eseguono solo tre (o N) processi indipendenti che leggono il file e calcolano diversi riepiloghi. Se "il file" è stato effettivamente generato al volo o scaricato da qualche sito remoto, o qualche altro processo lento, potrebbe avere senso fare le cose nel modo in cui si sta provando a farlo, ma se il file è presente in locale disco, è piuttosto probabile che solo un accesso al disco possa effettivamente accadere; i riepiloghi in ritardo leggeranno il file dalla cache del buffer. Se questo è tutto ciò che serve, GNU parallel dovrebbe funzionare bene, oppure puoi semplicemente avviare i processi in bash (con &) e poi aspettarli. YMMV, ma penso che una di queste soluzioni sarà meno dispendiosa in termini di risorse rispetto all'impostazione di tutti quei pipe e alla simulazione della cache del buffer in userland con tee.

A proposito, se si desidera serializzare l'output da più processi, è possibile utilizzare l'utilità flock. Usare solo un fifo non è abbastanza; non c'è alcuna garanzia che i processi che scrivono alla fifo scriveranno intere righe atomicamente e se tu sapessi che l'hanno fatto, non avresti avuto bisogno della fifo.