2009-07-03 8 views
6

Sto scrivendo script di shell in cui abbastanza regolarmente alcune cose vengono scritte su un file , dopo di che viene eseguita un'applicazione che legge quel file. Trovo che attraverso la nostra azienda la latenza della rete sia molto diversa, quindi un semplice sleep 2 per esempio non sarà abbastanza robusto.Come gestire la latenza NFS negli script di shell

ho cercato di scrivere un (configurabile) ciclo di timeout in questo modo:

waitLoop() 
{ 
    local timeout=$1 
    local test="$2" 

    if ! $test 
    then 
     local counter=0 
     while ! $test && [ $counter -lt $timeout ] 
     do 
     sleep 1 
     ((counter++)) 
     done 

     if ! $test 
     then 
     exit 1 
     fi 
    fi 
} 

Questo funziona per test="[ -e $somefilename ]". Tuttavia, testare l'esistenza non è sufficiente, a volte ho bisogno di verificare se una determinata stringa è stata scritta nel file. Ho provato test="grep -sq \"^sometext$\" $somefilename", ma questo non ha funzionato. Qualcuno può dirmi perché?

Esistono altre opzioni meno dettagliate per eseguire tale test?

+0

Sarebbe possibile eseguire semplicemente coda sul file e controllare solo l'output di coda (ad esempio le ultime righe)? Questo presuppone che tail esegua un controllo più intelligente dello script per verificare quando un file cambia (e le possibilità sono, coda lo fa in un modo più corretto) – nos

+0

Penso che il giusto approccio dipenda da cosa sia "qualche roba" e che cosa la "domanda" è. Puoi dare ulteriori dettagli sul vero problema che stai cercando di risolvere? La domanda come è scritta è un po 'vaga; potrebbero esserci approcci completamente diversi a cui non hai pensato. –

+0

Vedere la mia risposta modificata. –

risposta

1

È possibile impostare la variabile di test in questo modo:

test=$(grep -sq "^sometext$" $somefilename) 

Il motivo per il vostro grep non funziona è che le citazioni sono davvero difficili da passare in argomenti. Avrete bisogno di usare eval:

if ! eval $test 
0

Direi il modo per verificare se una stringa in un file di testo è grep.

Qual è il tuo problema esatto?

Inoltre, è possibile regolare i parametri di montaggio NFS per eliminare il problema di root. Potrebbe anche essere utile una sincronizzazione. Vedi documenti NFS.

+0

Non ho la possibilità di modificare i parametri di montaggio. Il mio script dovrebbe essere abbastanza robusto da gestire la latenza. Il problema non è con grep stesso, ma piuttosto che il test grep non viene valutato come mi aspetterei. –

+0

Ok, se non puoi cambiare il tuo ambiente, allora è un problema. In ogni caso, ha provato una "sincronizzazione" dopo aver modificato il file? Secondo i documenti NFS dovrebbe farlo (a patto che chiami sysnc.2). – TheBonsai

0

Se si desidera utilizzare waitLoop in un "se", è possibile modificare "uscita" in "ritorno", in modo che il resto dello script possa gestire la situazione di errore (non c'è nemmeno un messaggio all'utente di ciò che è fallito prima che lo script muoia in altro modo).

L'altro problema è usare "$ test" per mantenere un comando significa che non si ottiene l'espansione della shell durante l'esecuzione, ma solo la valutazione. Quindi se dici test = "grep \" foo \ "\" bar baz \ "", piuttosto che cercare la stringa di tre lettere foo nel file con la barra dei sette caratteri baz, cercherà la stringa di cinque caratteri "foo" nel file di nove caratteri "bar baz".

Così si può decidere o non è necessario la magia della shell, e test di set = 'grep -sq^someText $ somefilename', o si può ottenere la shell per gestire l'citando esplicitamente con qualcosa di simile:

if /bin/sh -c "$test" 
then 
    ... 
0

provare a utilizzare il tempo di modifica del file per rilevare quando è scritto senza aprirlo. Qualcosa come

old_mtime=`stat --format="%Z" file` 
# Write to file. 
new_mtime=$old_mtime 
while [[ "$old_mtime" -eq "$new_mtime" ]]; do 
    sleep 2; 
    new_mtime=`stat --format="%Z" file` 
done 

Ciò non funzionerà, tuttavia, se più processi tentano di accedere al file contemporaneamente.

0

Ho appena avuto lo stesso identico problema. Ho usato un approccio simile per l'attesa timeout che includi nel tuo OP; tuttavia, ho incluso anche un controllo della dimensione del file. Ho resettato il mio timer di timeout se il file era aumentato di dimensioni dall'ultima volta che è stato controllato. I file che sto scrivendo possono essere un paio di concerti, quindi richiedono un po 'di tempo per scrivere su NFS.

Questo potrebbe essere eccessivo per il tuo caso particolare, ma ho anche avuto il mio processo di scrittura calcolare un hash del file dopo che è stato fatto scrivere. Ho usato md5, ma qualcosa come crc32 avrebbe funzionato, anche. Questo hash è stato trasmesso dallo scrittore ai (più) lettori, e il lettore attende fino a) la dimensione del file smette di aumentare e b) l'hash (appena calcolato) del file corrisponde all'hash inviato dallo scrittore.

+0

grazie. Come si trasmette l'hash MD5? E un ciclo di attesa per questo hash non sarebbe sufficiente da solo? –

+0

Utilizziamo un protocollo di messaggistica di rete off-shelf (POE per Perl, ma JMS o AMQP, per questa funzionalità, sono gli stessi). Nel nostro caso, lo scrittore e i lettori sono su macchine diverse e lo scrittore trasmette l'hash come parte del messaggio che indica che la scrittura è completa. I lettori raccolgono il messaggio, attendono che la loro visualizzazione del file smetta di aumentare di dimensioni e quindi controllano l'hash. –

+0

E, sì, anche un ciclo di attesa sull'hash funzionerebbe - ma il calcolo dell'hash è relativamente costoso rispetto alla verifica della dimensione del file, quindi faccio la cosa facile/economica come un "gatekeeper" alla cosa pesante/costosa . –

0

Abbiamo un problema simile, ma per ragioni diverse. Stiamo leggendo il file s, che viene inviato a un server SFTP. La macchina che esegue lo script non è il server SFTP.

Quello che ho fatto è impostato su cron (anche se un loop con sleep funzionerebbe anche) per fare un cksum del file. Quando il vecchio cksum corrisponde al cksum corrente (il file non è cambiato per un determinato periodo di tempo), sappiamo che le scritture sono complete e trasferiamo il file.

Solo per essere più sicuri, non sovrascriviamo mai un file locale prima di fare un backup, e trasferiamo solo quando il file remoto ha due cksum in una riga che corrispondono, e quella cksum non corrisponde al file locale.

Se sono necessari esempi di codice, sono sicuro di poterli analizzare.

0

Il guscio stava dividendo il predicato in parole. Prendete il tutto con [email protected] come nel codice qui sotto:

#! /bin/bash 

waitFor() 
{ 
    local tries=$1 
    shift 
    local predicate="[email protected]" 

    while [ $tries -ge 1 ]; do 
    ((tries--)) 

    if $predicate >/dev/null 2>&1; then 
     return 
    else 
     [ $tries -gt 0 ] && sleep 1 
    fi 
    done 

    exit 1 
} 

pred='[ -e /etc/passwd ]' 
waitFor 5 $pred 
echo "$pred satisfied" 

rm -f /tmp/baz 
(sleep 2; echo blahblah >>/tmp/baz) & 
(sleep 4; echo hasfoo >>/tmp/baz) & 

pred='grep ^hasfoo /tmp/baz' 
waitFor 5 $pred 
echo "$pred satisfied" 

uscita:

 
$ ./waitngo 
[ -e /etc/passwd ] satisfied 
grep ^hasfoo /tmp/baz satisfied 

Peccato che il dattiloscritto non è così interessante come guardare in tempo reale.

-1

Ok ... questo è un po 'sgargianti ...

Se si ha il controllo del file: si potrebbe essere in grado di creare un 'named pipe' qui. Quindi (a seconda di come funziona il programma di scrittura) è possibile monitorare il file in modo sincronizzato.

Nel caso più semplice:

Creare il named pipe:

mkfifo file.txt 

Impostare il ricevitore sincronizzati:

while : 
do 
    process.sh < file.txt 
end 

creare un test mittente:

echo "Hello There" > file.txt 

Il 'process.sh' è dove va la tua logica: questo bloccherà finché il mittente non avrà scritto il suo output. In teoria il programma di scrittura non ha bisogno di modifiche ....

ATTENZIONE: se il ricevitore non funziona per qualche motivo, potresti finire per bloccare il mittente!

Non sicuro che soddisfi le vostre esigenze qui, ma potrebbe valere la pena di essere esaminato.

Oppure per evitare la sincronizzazione, provare 'lsof'?

http://en.wikipedia.org/wiki/Lsof

Supponendo che si desideri solo leggere dal file quando nient'altro sta scrivendo ad esso (cioè, il processo di scrittura è terminata) - si potrebbe verificare se non altro ha handle di file ad esso?

+0

Buona idea, ma un punto da notare è che le named pipe non funzionano se il mittente e il destinatario si trovano su due host diversi, anche se hanno gli stessi punti di mount NFS. – dogbane

+0

doh! Dovrebbe aver letto correttamente il titolo - yup non funzionerà con mount NFS! – monojohnny