Script per monitorare la cartella per i nuovi file?


127

Come rilevare nuovi file in una cartella con uno script ? Vorrei elaborare i file non appena vengono creati nella cartella. È possibile farlo o devo programmare uno script con che controlli i nuovi file ogni minuto?


1
Rimuoverai i file dalla cartella una volta elaborati?
ztank1013,

Risposte:


151

Dovresti prendere in considerazione l'utilizzo inotifywait, ad esempio:

inotifywait -m /path -e create -e moved_to |
    while read path action file; do
        echo "The file '$file' appeared in directory '$path' via '$action'"
        # do something with the file
    done

In Ubuntu inotifywaitè fornito dal inotify-toolspacchetto. A partire dalla versione 3.13 (attualmente in Ubuntu 12.04) inotifywaitincluderà il nome file senza l'opzione -f. Potrebbe essere necessario forzare le versioni precedenti. Ciò che è importante notare è che l' -eopzione inotifywaitè il modo migliore per eseguire il filtro degli eventi. Inoltre, il readcomando può assegnare l'output posizionale in più variabili che è possibile scegliere di utilizzare o ignorare. Non è necessario utilizzare grep / sed / awk per preelaborare l'output.


1
Grande! L' inotifywaitera proprio quello che volevo.
ihatetoregister,

2
Voglio solo aggiornare questo. Non hai bisogno di awk per raggiungere questo obiettivo. puoi filtrare gli eventi con '-e create' e ottenere solo il nome del file facendo '-f% f' o il percorso completo usando '-f% w% f'. Quindi la prima riga dello script precedente diventa: inotifywait -m / path -f% w% f -e create |
Lugoues

2
@Lugoues e ora quando provi a usare -f ottieni The '--filename' option no longer exists. The option it enabled in earlier versions of inotifywait is now turned on by default.Quindi, non ti resta inotifywait -m /path -e create |che provare a modificare questa risposta.
Bruno Bronosky,

1
Ora c'è anche uno strumento portatile per questo chiamato fswatch. Non l'ho scritto, ma è open source e lo uso.

1
@Wender inotfiywait emette 3 informazioni su una singola riga quando viene attivato. Il built-in bash "read" legge la riga di input e assegna ciascuna delle tre informazioni a una variabile. Pertanto, il primo pezzo viene assegnato al percorso variabile, il secondo all'azione e il terzo al file. Avendo assegnato valori a tali variabili, sono quindi disponibili per essere utilizzati in un secondo momento (come nella riga dell'eco). Ulteriori informazioni: tldp.org/LDP/Bash-Beginners-Guide/html/sect_08_02.html
Tim


24

Ho appena risolto questo problema e non ho riscontrato grossi problemi, a parte una piccola possibilità di perdere file tra i controlli.

while true
do
       touch  ./lastwatch
       sleep 10
       find /YOUR/WATCH/PATH -cnewer ./lastwatch -exec SOMECOMMAND {} \;
done

Se l'elaborazione dei file non richiede troppo tempo, non dovresti perdere nessun nuovo file. Potresti anche fare da sfondo alle attività ... Non è a prova di proiettile, ma serve ad alcuni scopi senza strumenti esterni come inotify.


Buona pesca. L'ho migliorato un po 'per supportare gli spazi nei nomi dei file.
Michael Sacchi,

Assolutamente. Questa è la strada da percorrere. Non sono davvero sicuro del motivo per cui ho seguito quella strada, uso regolarmente -exec.
Michael Sacchi, il

non è in tempo reale. il realtime è sempre il migliore
Farhan,

3
La migliore soluzione se inotifynon è disponibile. Aggiungerei -type fper filtrare solo i file. In caso contrario verrà restituita anche la cartella.
Xiao Peng - ZenUML.com,

Sì, l' -f filenameopzione è fantastica. Quindi l'unica domanda rimasta è come farlo iniziare al riavvio. Lo userò con il mio impianto solare per fare in os.system("ssh me@mysystem ' ( touch /home/me/alarms/low24 ) '")modo che la creazione di questo file causi l'utilizzo del computer master espeake l'annuncio della bassa tensione. Mi invia già un'e-mail, ma poiché il mio sistema parla già l'ora nella parte superiore dell'ora, ha tutto il resto. askubuntu.com/questions/977613/…
SDsolar

19

Puoi usarlo watchnel tuo script

watch -n 0.1 ls <your_folder>

Monitora la tua cartella ed elenca tutto ciò che contiene ogni 0,1 secondi

Inconveniente

Non è in tempo reale, quindi se un file è stato creato ed eliminato in meno di 0,1 secondi, questo non funzionerebbe, watchsupporta solo minimo 0,1 secondi.


Era esattamente quello che stavo cercando di ricordare! Molte grazie!!
Joabe Lucena,

9

Suppongo che la cartella di destinazione (la chiamerò isemptysolo per comodità) sia vuota e stai aspettando che uno o più file vengano rilasciati lì.

Puoi usare il seguente comando:

ls -1A isempty | wc -l

solo per verificare se la cartella è ancora vuota, infatti restituirà uno 0 se non ci sono nuovi file (quindi la isemptycartella è ancora vuota) o, d'altra parte, restituirà un valore maggiore di 0 (in realtà il numero dei file attualmente nella cartella).

Detto questo, uno sciocco test if / then può rendere il resto del lavoro:

if [ $(ls -1A isempty | wc -l) -gt 0 ] ; then do_something ; fi

Ovviamente la do_somethingfunzione dovrà manipolare i file all'interno della isemptycartella e quindi rimuoverli dalla cartella stessa dopo l'elaborazione.

L'aggiunta di una riga come la seguente nel crontab eseguirà il controllo una volta al minuto e, do_somethingnaturalmente , attiverà l' azione se la cartella non è vuota:

* * * * *     if [ $(ls -1A isempty | wc -l) -gt 0 ] ; then do_something ; fi

Questa soluzione funziona con filesystem remoti montati. Gli sviluppatori di inotify-tools stanno lavorando su un fusibile (o lo erano a metà 2014).
Rondo,

3
Non dovresti mai usare lsper lo scripting. Usa findinvece il semplice globbing
andsens,

6

Se si desidera rilevare nuovi file, quindi elaborarli e alla fine eliminare i file proseguiti è possibile utilizzare systemd.path . Questo metodo si basa su inotify. C'è un'opzione DirectoryNotEmpty, quindi systemd può eseguire lo script sempre quando rileva qualsiasi file nella directory. Devi ricordare che funzionerà solo se puoi eliminare i file proceduti e lo script lascia vuota la directory.

Preparare innanzitutto il file mymonitor.service

[Unit]
Description=Start the script

[Service]
Type=oneshot
ExecStart=/path/to/your/script

quindi vai a mymonitor.path per definire il percorso

[Unit]
Description= Triggers the service

[Path]
DirectoryNotEmpty=/path/to/monitor

[Install]
WantedBy=multi-user.target

Se il nome del file .path è uguale al nome del servizio, non è necessario specificare il nome del servizio nel file .path.

Si basa sul monitoraggio dell'accesso ai file per i manichini


4

entr

L'utilizzo entrè il nuovo modo per farlo (è multipiattaforma). Nota entrnon utilizza il polling, il che gli dà un enorme vantaggio rispetto a molte delle alternative.

Utilizza kqueue(2)o inotify(7)per evitare il polling. entrè stato scritto per rendere il feedback rapido e i test automatizzati naturali e del tutto ordinari.

Su BSD utilizza pledge(2)

Puoi installarlo con

apt-get install entr
dnf install entr

È possibile tenere traccia di una directory per nuove aggiunte utilizzando

while $(true); do
  # echo ./my_watch_dir | entr -dnr echo "Running trigger..."
  echo ./my_watch_dir | entr -dnr ##MY COMMAND##
done;

Opzioni spiegate (dai documenti),

  • -d Tieni traccia delle directory dei file regolari forniti come input e chiudi se viene aggiunto un nuovo file. Questa opzione consente inoltre di specificare esplicitamente le directory. File con nomi che iniziano con '.' sono ignorati.
  • -nEsegui in modalità non interattiva. In questa modalità entr non tenta di leggere dal TTY né di modificarne le proprietà.
  • -r Ricarica un processo figlio persistente. Come per la modalità operativa standard, un'utilità che termina non viene più eseguita fino a quando non viene elaborato un file system o un evento della tastiera. SIGTERMviene utilizzato per terminare l'utilità prima che venga riavviata. Viene creato un gruppo di processi per impedire agli script della shell di mascherare i segnali. entrattende che l'utilità venga chiusa per assicurarsi che risorse come i socket siano state chiuse. Il controllo del TTY non trasferisce il processo figlio.

2

Bash non può farlo facilmente. Dovresti sostanzialmente ottenere un elenco di tutti i file nella cartella e periodicamente ottenere un nuovo elenco e confrontarli per vedere cosa è cambiato.

Quello che stai cercando si chiama inotify. È integrato nel kernel di Linux e puoi praticamente rimanere seduto lì in attesa che accada qualcosa a quel punto inotify ritorna e dice "hey, c'è un nuovo file chiamato foobar"

Per ottenere ciò che desideri dovresti passare a qualcosa come perl e usare Linux :: Inotify2 (probabilmente Python supporta anche inotify, ma io sono una persona perl).


0

Funziona su Cygwin e Linux. Alcune delle precedenti soluzioni che scrivono un file causeranno il crash del disco. Questa sceneggiatura non presenta questo problema:

SIG=1
SIG0=$SIG
while [ $SIG != 0 ] ; do
 while [ $SIG = $SIG0 ] ; do
   SIG=`ls -1 | md5sum | cut -c1-32`
   sleep 10
 done
 SIG0=$SIG
 ls -lrt | tail -n 1
done

0

Di seguito è riportata una versione ridotta dell'esempio su StackOverflow che ho testato e incorporato in uno dei miei progetti che richiede il monitoraggio di directory specifiche.

Var_dir="${1:-/tmp}"
Var_diff_sleep="${2:-120}"
Var_diff_opts="--suppress-common-lines"
Func_parse_diff(){
    _added="$(grep -E '>' <<<"${@}")"
    if [ "${#_added}" != "0" ]; then
        mapfile -t _added_list <<<"${_added//> /}"
        _let _index=0
        until [ "${#_added_list[@]}" = "${_index}" ]; do
            _path_to_check="${Var_dir}/${_added_list[${_index}]}"
            if [ -f "${_path_to_check}" ]; then
                echo "# File: ${_path_to_check}"
            elif [ -d "${_path_to_check}" ]; then
                echo "# Directory: ${_path_to_check}"
            if [ -p "${_path_to_check}" ]; then
                echo "# Pipe: ${_path_to_check}"
            fi
            let _index++
        done
        unset _index
    fi
}
Func_watch_bulk_dir(){
    _current_listing=""
    while [ -d "${Var_dir}" ]; do
        _new_listing="$(ls "${Var_dir}")"
        _diff_listing="$(diff ${Var_dec_diff_opts} <(${Var_echo} "${_current_listing}") <(${Var_echo} "${_new_listing}"))"
        if [ "${_diff_listing}" != "0" ]; then
            Func_parse_diff "${_diff_listing}"
        fi
        _current_listing="${_new_listing}"
        sleep ${Var_diff_sleep}
    done
}

Ecco un collegamento a uno script che utilizza una versione modificata di cui sopra per decrittografare automaticamente i file o le directory trovati nel suo punto di montaggio sshfs; il suddetto progetto.

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.