web-dev-qa-db-fra.com

Script pour surveiller le dossier pour les nouveaux fichiers?

Comment détecter de nouveaux fichiers dans un dossier avec un script bash ? Je voudrais 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 cron qui vérifie les nouveaux fichiers chaque minute ou plus?

141
ihatetoregister

Vous devriez envisager d'utiliser inotifywait, par exemple:

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

Dans Ubuntu inotifywait est fourni par le inotify-tools paquet. À partir de la version 3.13 (actuelle dans Ubuntu 12.04) inotifywait inclura le nom de fichier sans l'option -f. Les anciennes versions peuvent devoir être contraintes. Il est important de noter que le -e l'option pour inotifywait est la meilleure façon de filtrer les événements. De plus, votre commande read peut affecter la sortie positionnelle à 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.

168
enzotib

Je viens de préparer cela, et je ne vois pas d'énormes problèmes avec cela, à part une petite chance de manquer des fichiers entre les vérifications.

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

Si le traitement de vos fichiers ne prend pas trop de temps, vous ne devriez manquer aucun nouveau fichier. Vous pouvez également décrire les activités ... Ce n'est pas à l'épreuve des balles, mais cela sert à certaines fins sans outils externes comme inotify.

28
Michael Sacchi

Je préfère incron , car c'est plus facile à gérer. Il s'agit essentiellement d'un service qui exploite inotify et vous pouvez configurer des configurations pour prendre des mesures en fonction des opérations de modification 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/

27
rynop

Vous pouvez utiliser watch dans votre script

watch -n 0.1 ls <your_folder>

Surveille votre dossier et vous 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 a été créé et supprimé en moins de 0,1 seconde, cela ne fonctionnerait pas, watch ne prend en charge qu'un minimum de 0,1 seconde.

26
GypsyCosmonaut

Je suppose que le dossier cible (je l'appellerai isempty juste pour plus de commodité) est vide et 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 (d'où le dossier isempty est toujours vide) ou, d'autre part, il retournera une valeur supérieure de 0 (en fait le nombre de 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 sûr, la fonction do_something Devra manipuler le (s) fichier (s) dans le dossier isempty puis le (les) supprimer du dossier lui-même après le traitement.

L'ajout d'une ligne comme celle-ci dans votre crontab exécutera la vérification une fois par minute et déclenchera l'action do_something Si le dossier n'est pas vide bien sûr:

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

entr

Utiliser entr est la nouvelle façon de le faire (c'est multi-plateforme). Remarque entr n'utilise pas l'interrogation, ce qui lui donne un énorme avantage sur de nombreuses alternatives.

Utilise kqueue(2) ou inotify(7) pour éviter l'interrogation. entr a été écrit pour rendre la rétroaction rapide et les tests automatisés naturels et complètement ordinaires.

Sur BSD, il utilise pledge(2)

Vous pouvez l'installer avec

apt-get install entr
dnf install entr
brew install entr

Vous pouvez suivre un répertoire pour de 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;

Explication des options (à partir des documents),

  • -d Suivez les répertoires des fichiers normaux fournis en entrée et quittez si un nouveau fichier est ajouté. Cette option permet également de spécifier explicitement les répertoires. Les fichiers dont le nom commence par "." Sont ignorés.
  • -n Exécuter en mode non interactif. Dans ce mode, entr n'essaie pas de lire le TTY ni de modifier ses propriétés.
  • -r Recharger un processus enfant persistant. Comme pour le mode de fonctionnement standard, un utilitaire qui se termine n'est pas exécuté de nouveau tant qu'un événement de système de fichiers ou de clavier n'est pas traité. SIGTERM est utilisé pour terminer l'utilitaire avant son redémarrage. Un groupe de processus est créé pour empêcher les scripts Shell de masquer les signaux. entr attend la fermeture de l'utilitaire pour s'assurer que les ressources telles que les sockets ont été fermées. Le contrôle du TTY n'est pas transféré au processus enfant.
8
Evan Carroll

Si vous voulez détecter de nouveaux fichiers, puis traitez-les et à la fin supprimez les fichiers en cours, vous pouvez utiliser systemd.path . Cette méthode repose sur inotify. Il y a une option DirectoryNotEmpty, donc systemd peut exécuter votre script toujours quand il détecte des fichiers 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.

Préparez d'abord le fichier mymonitor.service

[Unit]
Description=Start the script

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

allez ensuite 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 le même que le nom du service, il n'est pas nécessaire de spécifier le nom du service dans le fichier .path.

Il se base sur Surveillance de l'accès aux fichiers pour les nuls

7
Dawid Wolski

Bash ne peut pas faire cela facilement. Vous devez essentiellement obtenir une liste de tous les fichiers du dossier et obtenir périodiquement une nouvelle liste et les comparer pour voir ce qui a changé.

Ce que vous recherchez s'appelle inotify. Son intégré dans le noyau Linux et vous pouvez essentiellement rester là en attendant que quelque chose se produise, à quel point 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 Perl).

2
Patrick

Cela fonctionne sous cygwin et Linux. Certaines des solutions précédentes qui écrivent un fichier provoquent le thrash du disque. Cette 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
0
user1186515

Ci-dessous se trouve une version abrégée de l'exemple sur stackoverflow que j'ai testé et incorporé dans l'un de mes projets qui nécessite 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 ci-dessus pour décrypter automatiquement les fichiers ou répertoires trouvés dans son point de montage sshfs; le projet susmentionné.

0
S0AndS0