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.