Script pour surveiller le dossier pour les nouveaux fichiers?

127

Comment détecter de nouveaux fichiers dans un dossier avec un script ? Je souhaite traiter les fichiers dès qu'ils sont créés dans le dossier. Est-ce possible de le faire ou dois-je planifier un script avec qui recherche de nouveaux fichiers toutes les minutes environ?

ihatetorgister
la source
1
Allez-vous supprimer les fichiers du dossier une fois qu'ils sont traités?
Ztank1013

Réponses:

151

Vous devriez envisager d'utiliser inotifywait, à titre d'exemple:

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

Dans Ubuntu inotifywaitest fourni par le inotify-toolspaquet. À partir de la version 3.13 (actuelle dans Ubuntu 12.04) inotifywait, le nom du fichier ne sera pas inclus avec l'option -f. Les anciennes versions devront peut-être être contraintes. Il est important de noter que l’ -eoption to inotifywaitest la meilleure façon de filtrer les événements. En outre, votre readcommande peut affecter la sortie de position à plusieurs variables que vous pouvez choisir d'utiliser ou d'ignorer. Il n'est pas nécessaire d'utiliser grep / sed / awk pour prétraiter la sortie.

enzotib
la source
1
Génial! Le inotifywaitétait exactement ce que je voulais.
ihatetoregister
2
Je veux juste mettre à jour cela. Vous n'avez pas besoin de awk pour y parvenir. vous pouvez filtrer les événements avec '-e create' et obtenir uniquement le nom du fichier en effectuant '-f% f' ou le chemin complet en utilisant '-f% w% f'. Ainsi, la première ligne du script ci-dessus devient: inotifywait -m / chemin -f% w% f -e create |
Lugoues
2
@Lugoues et maintenant, quand vous essayez d'utiliser -f vous obtenez The '--filename' option no longer exists. The option it enabled in earlier versions of inotifywait is now turned on by default.Donc, il vous suffit de le faire, inotifywait -m /path -e create |je vais essayer de modifier cette réponse.
Bruno Bronosky
1
Maintenant, il existe également un outil portable pour cela appelé fswatch. Je ne l'ai pas écrit, mais il est open source et je l'utilise.
1
@Wender inotfiywait génère 3 informations sur une seule ligne lorsqu’elles sont déclenchées. La commande 'read' intégrée à bash lit la ligne d'entrée et affecte chacune des trois informations à une variable. Ainsi, le premier élément est affecté à la variable path, le second à l'action et le troisième à archiver. Ayant affecté des valeurs à ces variables, elles sont ensuite disponibles pour être utilisées plus tard (comme sur la ligne d'écho). Plus d'informations: tldp.org/LDP/Bash-Beginners-Guide/html/sect_08_02.html
Tim
26

Je préfère incron, car c'est plus facile à gérer. Pour l’essentiel, c’est un service qui exploite inotifyet vous pouvez configurer des configurations pour prendre des mesures en fonction des opérations de changement de fichier.

Ex:

<directory> <file change mask> <command or action>  options
/var/www/html IN_CREATE /root/scripts/backup.sh

Vous pouvez voir un exemple complet ici: http://www.cyberciti.biz/faq/linux-inotify-examples-to-replicate-directories/

rynop
la source
24

Je viens de cuire cela, et je ne vois pas de gros problèmes, à part un risque minime de fichiers manquants entre les vérifications.

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

Si le traitement de votre fichier ne prend pas trop de temps, vous ne devez manquer aucun nouveau fichier. Vous pouvez également mettre en contexte les activités ... Ce n’est pas une solution à toute épreuve, mais cela sert à quelque chose sans outils externes comme inotify.

Michael Sacchi
la source
Bonne prise. Je l'ai un peu amélioré pour prendre en charge les espaces dans les noms de fichiers.
Michael Sacchi
Absolument. C'est le chemin à parcourir. Pas vraiment sûr pourquoi je suis allé dans cette voie, j'utilise -exec régulièrement.
Michael Sacchi
ce n'est pas en temps réel. le temps réel est toujours le meilleur
Farhan
3
Meilleure solution si inotifyn'est pas disponible. J'ajouterais -type fpour filtrer les fichiers uniquement. Sinon, le dossier sera également renvoyé.
Xiao Peng - ZenUML.com
Oui, l' -f filenameoption est géniale. Alors, la seule question qui reste est de savoir comment faire en sorte que cela démarre au redémarrage. Je vais utiliser cela avec mon installation solaire pour os.system("ssh me@mysystem ' ( touch /home/me/alarms/low24 ) '")que la création de ce fichier oblige l'ordinateur maître à utiliser espeaket à annoncer la basse tension. Il m'envoie déjà un email mais comme mon système parle déjà l'heure en haut de l'heure, il a tout le reste. askubuntu.com/questions/977613/…
SDsolar
19

Vous pouvez utiliser watchdans votre script

watch -n 0.1 ls <your_folder>

Surveille votre dossier et répertorie tout ce qu'il contient toutes les 0,1 secondes

Inconvénient

N'est pas en temps réel, donc si un fichier était créé et supprimé en moins de 0,1 seconde, cela ne fonctionnerait pas, watchne prend en charge qu'un minimum de 0,1 seconde.

GypsyCosmonaut
la source
C'était exactement ce que j'essayais de retenir! Merci beaucoup!!
Joabe Lucena
9

Je suppose que le dossier cible (je l'appellerai isemptysimplement pour plus de commodité) est vide et que vous attendez qu'un ou plusieurs fichiers y soient déposés.

Vous pouvez utiliser la commande suivante:

ls -1A isempty | wc -l

juste pour vérifier si le dossier est toujours vide, en fait il retournera un 0 s'il n'y a pas de nouveau fichier (donc le isemptydossier est toujours vide) ou, au contraire, il retournera une valeur supérieure à 0 (en fait le nombre des fichiers actuellement dans le dossier).

Cela dit, un test idiot si / alors peut faire le reste du travail:

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

Bien entendu, la do_somethingfonction devra manipuler le ou les fichiers du isemptydossier, puis les supprimer du dossier après le traitement.

Ajouter une ligne comme celle-ci dans votre crontab lancera le contrôle une fois par minute et déclenchera l' do_somethingaction si le dossier n'est pas vide bien sûr:

* * * * *     if [ $(ls -1A isempty | wc -l) -gt 0 ] ; then do_something ; fi
ztank1013
la source
Cette solution fonctionne pour les systèmes de fichiers distants montés. Le ou les développeurs d’inotify-tools travaillent sur la fusion (ou l’étaient à la mi-2014).
Rondo
3
Vous ne devriez jamais utiliser lspour les scripts. Utilisez findou un simple "globbing" à la place: mywiki.wooledge.org/ParsingLs
andsens
6

Si vous souhaitez détecter de nouveaux fichiers, traitez-les puis, à la fin de la suppression des fichiers, vous pouvez utiliser systemd.path . Cette méthode repose sur inotify. Il existe une option DirectoryNotEmpty, afin que systemd puisse toujours exécuter votre script lorsqu'il détecte un fichier dans le répertoire. Vous devez vous rappeler que cela ne fonctionnera que si vous pouvez supprimer les fichiers traités et que le script laisse le répertoire vide.

Commencez par préparer le fichier mymonitor.service

[Unit]
Description=Start the script

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

Ensuite, allez dans mymonitor.path pour définir le chemin.

[Unit]
Description= Triggers the service

[Path]
DirectoryNotEmpty=/path/to/monitor

[Install]
WantedBy=multi-user.target

Si le nom du fichier .path est identique au nom du service, il n'est pas nécessaire de spécifier le nom du service dans le fichier .path.

Il repose sur la surveillance de l'accès aux fichiers pour les nuls

Dawid Wolski
la source
4

entr

Utiliser entrest la nouvelle façon de faire (c'est multi-plateforme). Note entrn'utilise pas l'interrogation, ce qui lui confère un avantage considérable sur de nombreuses alternatives.

Utilisations kqueue(2)ou inotify(7)pour éviter de voter. entra été écrit pour rendre les commentaires rapides et les tests automatisés naturels et tout à fait ordinaires.

Sur BSD, il utilise pledge(2)

Vous pouvez l'installer avec

apt-get install entr
dnf install entr

Vous pouvez suivre un répertoire pour les nouveaux ajouts en utilisant

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

Options expliquées (à partir de la documentation),

  • -d Suivre les répertoires des fichiers standard fournis en entrée et quitter si un nouveau fichier est ajouté. Cette option permet également de spécifier explicitement les répertoires. Fichiers avec les noms commençant par '.' sont ignorés.
  • -nExécuter en mode non interactif. Dans ce mode, entr ne tente pas de lire le téléscripteur ni de modifier ses propriétés.
  • -r Recharger un processus enfant persistant. Comme avec le mode de fonctionnement standard, un utilitaire qui se termine n'est pas exécuté à nouveau jusqu'à ce qu'un événement de système de fichiers ou de clavier soit traité. SIGTERMest utilisé pour mettre fin à l'utilitaire avant qu'il ne soit redémarré. Un groupe de processus est créé pour empêcher les scripts de masquer les signaux. entrattend que l'utilitaire se ferme pour s'assurer que des ressources telles que des sockets ont été fermées. Le contrôle de l'ATS n'est pas transféré au processus enfant.
Evan Carroll
la source
2

Bash ne peut pas le faire facilement. Il vous faudrait essentiellement obtenir une liste de tous les fichiers du dossier, obtenir périodiquement une nouvelle liste et les comparer pour voir ce qui a changé.

Ce que vous recherchez s'appelle inotify. Il est intégré au noyau Linux et vous pouvez rester assis à attendre que quelque chose se passe. Inotify revient et dit: "Hé, il y a un nouveau fichier appelé foobar".

Pour accomplir ce que vous voulez, vous devez passer à quelque chose comme perl et utiliser Linux :: Inotify2 (python supporte probablement aussi inotify, mais je suis une personne de Perl).

Patrick
la source
0

Cela fonctionne sous cygwin et Linux. Certaines des solutions précédentes qui écrivent un fichier vont provoquer le thrash du disque. Ce scipt n'a pas ce problème:

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
utilisateur1186515
la source
0

Vous trouverez ci-dessous une version abrégée de l'exemple sur stackoverflow que j'ai testée et intégrée à l'un de mes projets nécessitant la surveillance de répertoires spécifiques.

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
}

Voici un lien vers un script qui utilise une version modifiée de celle-ci pour déchiffrer automatiquement les fichiers ou les répertoires situés dans son point de montage sshfs; le projet mentionné ci-dessus.

S0AndS0
la source