Sposta il file ma solo se è chiuso


10

Voglio spostare file di grandi dimensioni creati da un processo esterno non appena viene chiuso.

Questo comando di test è corretto?

if lsof "/file/name"
then
        # file is open, don't touch it!
else
        if [ 1 -eq $? ]
        then
                # file is closed
                mv /file/name /other/file/name
        else
                # lsof failed for some other reason
        fi
fi

EDIT: il file rappresenta un set di dati e devo aspettare fino a quando non è completo per spostarlo in modo che un altro programma possa agire su di esso. Ecco perché devo sapere se il processo esterno è stato eseguito con il file.


3
Nota a margine: una volta aperto un file, i processi utilizzano descrittori di file e dati di inode per manipolarlo. La modifica del percorso (ovvero lo spostamento del file) non causerà troppi problemi al processo.
John WH Smith,

2
Hai qualche controllo sul processo esterno? Sarebbe possibile per il processo esterno creare un file temporaneo e rinominarlo dopo averlo scritto?
Jenny D,

@JennyD Ho fatto qualche indagine e risulta essere vero. Non mi serve lsofaffatto, devo solo controllare se l'estensione del file non lo è .tmp. Questo lo rende banale. Tuttavia sono contento di aver chiesto la mia domanda da quando ho imparato un po 'su lsofe inotifye roba.
Peter Kovac,

@PeterKovac Ho imparato di più anche su di loro, leggendo le risposte, quindi sono molto felice che tu l'abbia chiesto.
Jenny D,

@JohnWHSmith - Questo è normalmente vero se sposta il file all'interno dello stesso filesystem, se sposta il file in un nuovo filesystem prima che lo scrittore abbia finito di scriverlo, perderà alcuni dati.
Johnny,

Risposte:


11

Dalla lsofpagina man

Lsof restituisce uno (1) se viene rilevato un errore, incluso l'incapacità di individuare nomi di comandi, nomi di file, indirizzi o file Internet, nomi di accesso, file NFS, PID, PGID o UID che è stato richiesto di elencare. Se viene specificata l'opzione -V, lsof indicherà gli elementi di ricerca che non è stato elencato.

Questo suggerirebbe che la tua lsof failed for some other reasonclausola non sarebbe mai stata eseguita.

Hai provato a spostare il file mentre il tuo processo esterno è ancora aperto? Se la directory di destinazione si trova sullo stesso filesystem, non dovrebbero esserci problemi a farlo a meno che non sia necessario accedervi dal percorso originale da un terzo processo poiché l'inode sottostante rimarrà lo stesso. Altrimenti penso mvche fallirà comunque.

Se hai davvero bisogno di aspettare fino al termine del processo esterno con il file, è meglio usare un comando che blocca invece di eseguire ripetutamente il polling. Su Linux, puoi usarlo inotifywaitper questo. Per esempio:

 inotifywait -e close_write /path/to/file

Se devi usare lsof(forse per la portabilità), potresti provare qualcosa del tipo:

until err_str=$(lsof /path/to/file 2>&1 >/dev/null); do
  if [ -n "$err_str" ]; then
    # lsof printed an error string, file may or may not be open
    echo "lsof: $err_str" >&2

    # tricky to decide what to do here, you may want to retry a number of times,
    # but for this example just break
    break
  fi

  # lsof returned 1 but didn't print an error string, assume the file is open
  sleep 1
done

if [ -z "$err_str" ]; then
  # file has been closed, move it
  mv /path/to/file /destination/path
fi

Aggiornare

Come notato da @JohnWHSmith di seguito, il progetto più sicuro userebbe sempre un lsofciclo come sopra poiché è possibile che più di un processo abbia il file aperto per la scrittura (un caso di esempio potrebbe essere un demone di indicizzazione mal scritto che apre i file con la lettura / scrivi flag quando dovrebbe davvero essere di sola lettura). inotifywaitpuò comunque essere usato al posto del sonno, basta sostituire la linea del sonno con inotifywait -e close /path/to/file.


Grazie, non ne ero a conoscenza inotify. Sfortunatamente, non è installato sulla mia scatola ma sono sicuro che troverò un pacchetto da qualche parte. Vedi la mia modifica per un motivo per cui ho bisogno che il file sia chiuso: è un set di dati e deve essere completo prima di elaborarlo ulteriormente.
Peter Kovac,

1
Un'altra nota a margine: mentre inotifywaitimpedirà allo script di "pollare" spesso due, l'OP deve comunque effettuare il check in lsofin un ciclo: se il file viene aperto due volte, la chiusura una volta potrebbe attivare l' inotifyevento, anche se il file non è pronto per essere manipolato (ad esempio, nell'ultimo frammento di codice, la sleepchiamata potrebbe essere sostituita con inotifywait).
John WH Smith,

@John a close_writedovrebbe essere ok poiché solo un processo può avere il file aperto per la scrittura alla volta. Presuppone che un altro non lo aprirà subito dopo la chiusura, ma lo stesso problema esiste con il lsofpolling.
Graeme,

1
@Graeme Anche se questo potrebbe essere vero in base alla progettazione nel caso dell'OP, il kernel consente di aprire un file due volte per la scrittura (nel qual caso, CLOSE_WRITEviene attivato due volte).
John WH Smith,

@John, aggiornato.
Graeme,

4

Come approccio alternativo, questo è il caso perfetto per una pipe : il secondo processo elaborerà l'output del primo processo non appena sarà disponibile, anziché attendere il completamento dell'intero processo:

process1 input_file.dat | process2 > output_file.dat

vantaggi:

  • Molto più veloce in generale:
    • Non è necessario scrivere e leggere dal disco (questo può essere evitato se si utilizza un ramdisk).
    • Dovrebbe utilizzare le risorse della macchina in modo più completo.
  • Nessun file intermedio da rimuovere dopo la finitura.
  • Nessun blocco complesso necessario, come in OP.

Se non hai modo di creare direttamente una pipe ma hai coreutils GNU puoi usare questo:

tail -F -n +0 input_file.dat | process2 > output_file.dat

Ciò inizierà a leggere il file di input dall'inizio, indipendentemente dalla distanza del primo processo attraverso la scrittura del file (anche se non è ancora stato avviato o è già finito).


Sì, sarebbe la soluzione "ovvia". Sfortunatamente, il processo di generazione dei dati è fuori dal mio controllo (gestito da un altro utente).
Peter Kovac,

@PeterKovac È irrilevante: cat input_file.dat | process2 output_file.dat
MariusMatutiae

@MariusMatutiae ma cate process2potrebbe finire prima che process1sia finito. Non avrebbero bloccato.
cpugeniusmv,
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.