Determinare se il file è in fase di scrittura?


25

Ho bisogno di distribuire un processo automatizzato (tramite cron script di 1 minuto) che cerca i file tar in una directory specifica. Se viene trovato un file tar, non viene tarato nella posizione appropriata e quindi il file tar viene eliminato.

I file tar vengono automaticamente copiati su questo server tramite SSH da un altro server. In alcuni casi, i file tar sono estremamente grandi, con molti file.

Il problema che mi aspetto di incontrare: se ci vogliono> 1 minuto per copiare il file tar sul server e lo script cron viene eseguito una volta al minuto, vedrà il file .tar.gz e proverà a fare decomprimerlo, anche se il file tar è ancora in fase di scrittura.

Esiste un modo (tramite comandi bash) per verificare se un file è attualmente in fase di scrittura o se si tratta solo di un file parziale, ecc.?

Un'alternativa a cui stavo pensando era quella di far copiare il file come una diversa estensione (come .tar.gz.part) e poi rinominarlo .tar.gzdopo che il trasferimento è completo. Ma ho pensato che avrei provato a capire se esiste semplicemente un modo per determinare se il file è intero nella riga di comando prima ... Qualche indizio?


2
Come viene trasferito esattamente il file? Ad esempio, rsyncutilizza un nome file temporaneo durante il trasferimento (per impostazione predefinita) e solo dopo che il file è stato completamente trasferito, lo rinomina nel nome file effettivo.
Piskvor,

Risposte:


12

Sei sulla buona strada, rinominare il file è un'operazione atomica, quindi eseguire la ridenominazione dopo il caricamento è semplice, elegante e non soggetto a errori. Un altro approccio che mi viene in mente è quello di utilizzare lsof | grep filename.tar.gzper verificare se il file è accessibile da un altro processo.


7
( lsof filename.tar.gzè più efficiente e più preciso di lsof | grep filename.tar.gz)
Rich

A proposito, dovrebbe essere un percorso assoluto del nome file
DennisLi

14

La soluzione migliore è utilizzare lsofper determinare se un file è stato aperto da qualsiasi processo:

#  lsof -f -- /var/log/syslog
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF  NODE NAME
rsyslogd 1520 syslog    1w   REG  252,2    72692 16719 /var/log/syslog

Non si può facilmente dire se è in fase di scrittura, ma se viene scritto, DEVE essere aperto.


Modifica: risolviamo qui il problema reale anziché provare a implementare la soluzione proposta!

Usa rsync per trasferire il file:

  rsync -e ssh remote:big.tar.gz .

In questo modo, il file non verrà copiato sopra a quello esistente ma copiato in un file temporaneo ( .big.tar.gz.XXXXXX) fino al completamento del trasferimento, quindi spostato in posizione.


6

Un po 'vecchio, ma la maggior parte delle risposte manca completamente il punto della domanda:

Ma ho pensato che avrei provato a capire se c'è semplicemente un modo per determinare se il file è intero nella riga di comando prima ...

In generale, non c'è. Semplicemente non hai abbastanza informazioni per determinarlo.

Perché determinare che il file è chiuso non equivale a determinare se il file è intero . Ad esempio, un file verrà "chiuso" se la connessione viene persa durante il trasferimento.

Solo la risposta di Alex ha capito bene. E anche lui è caduto per aver usato lsofun po '.

Per determinare se il file è stato completamente, il trasferimento corretto richiede più dati. Ad esempio:

Un'alternativa a cui stavo pensando era quella di far copiare il file come una diversa estensione (come .tar.gz.part) e poi rinominarlo .tar.gzdopo che il trasferimento è completo.

È un modo perfetto per comunicare che il file è stato trasferito in modo completo e corretto. Puoi anche spostare i file da una directory all'altra purché rimanga all'interno dello stesso filesystem. Oppure chiedi al mittente di inviare un filename.donefile vuoto per segnalare il completamento.

Ma tutti i metodi devono fare affidamento sul mittente in qualche modo segnalando che il trasferimento è stato completato correttamente. Perché solo il mittente ha tali informazioni.

Alcuni formati di file (come i PDF) contengono dati che consentono di determinare se il file è completo. Ma devi aprire e leggere praticamente l'intero file per scoprirlo.

lsofti dirà semplicemente che il file non è più aperto - non ti dirà perché non è più aperto. Né ti dirà quanto dovrebbe essere grande il file.


1
Non posso votare abbastanza. Ottimo lavoro risolvendo il problema XY qui.
Beefster,

5

Il modo migliore per farlo è usare incron ("inotify cron system"). Ti consente di impostare un controllo inotify su una directory che ti avviserà delle operazioni sui file. In questo caso, dovresti guardare la directory per close_write. Ciò ti consentirà quindi di eseguire il comando una volta che il file è stato chiuso dopo una scrittura.


2

Sembra che lsof sia in grado di rilevare in quale modalità è aperto un file:

lsof -f -- a_file
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
cat     52391 bob    1w   REG    1,2       15 19545007 a_file

Vedi dove dice 1w? Ciò significa che il numero del descrittore di file è 1 e la modalità è w, oppure scrivi.


Il FDcampo mostra 3rper me quando il file è aperto per la lettura.
Sopalajo de Arrierez,

0

L'utilizzo inotifywaitpuò ottenere ciò che stai cercando: ha la capacità di attendere fino al termine della scrittura di un file prima di eseguire un comando.

Quanto segue controllerà continuamente una cartella per i nuovi file ed eseguirà il comando nel ciclo al termine della scrittura sul file.

WATCH_DIR=/directory/to/monitor
DEST_DIR=/x/y/z

/usr/bin/inotifywait --recursive --monitor --quiet -e moved_to -e close_write --format '%w%f' "$WATCH_DIR" | while read -r INPUT_FILE; do

mv "$0" "$DEST_DIR"

done

Per ulteriori opzioni di configurazione, consultare https://linux.die.net/man/1/inotifywatch

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.