2010-10-30 31 views
46

Je souhaite exécuter un script shell lorsqu'un fichier ou un répertoire spécifique est modifié.Sous Linux, comment exécuter un script shell lorsqu'un fichier ou un répertoire change

Comment puis-je facilement faire cela?

+9

+1. Je pense que c'est sur le sujet (pas sûr pourquoi quelqu'un a voté pour passer au super-utilisateur - cette question est sur shell * programmation *), et une bonne question. –

+1

J'ai un post, je pense est fondamentalement le même: http://stackoverflow.com/q/2972765/119790 –

+1

@ MerlynMorgan-Graham Je déplacerais cela à superutilisateur, parce que la réponse pourrait ne rien avoir à faire avec la programmation - ie il peut y avoir une option de programme ou de configuration qui peut être utilisée, sans aucune programmation nécessaire. J'ai eu la même question, et j'ai d'abord recherché super-utilisateur: p – Benubird

Répondre

22

Utilisez inotify-tools.

+11

[incron] (http://inotify.aiken.cz/?section=incron&page=about&lang=en) est une autre option. –

+2

fanotify est une autre option. Construit sur le dessus d'inotify. Il a quelques améliorations à inotify, par exemple il peut notifier les changements de fichiers dans un répertoire spécifique. –

+7

Ce serait bien si cette réponse incluait l'information pertinente au lieu d'être juste un lien. – Darkhogg

2

Comme mentionné, inotify-tools est probablement la meilleure idée. Cependant, si vous programmez pour le plaisir, vous pouvez essayer de gagner des XPs hacker par une application judicieuse de tail -f.

+0

Il y a aussi [inotail] (http://distanz.ch/inotail/). –

+0

Intéressant, mais comment faites-vous cela? tail -f est une instruction de blocage, nous en avons besoin pour retourner afin de lancer un script? – pdem

+0

@pdem lit sa sortie - toute sortie signifierait que le fichier a changé. Je ne suis pas sûr de ce qu'il fait si le fichier * se rétrécit * si – Xen2050

-1

Que diriez-vous de ce script? Utilise la commande 'stat' pour obtenir l'heure d'accès d'un fichier et exécute une commande chaque fois qu'il y a un changement dans le temps d'accès (chaque fois qu'un fichier est accédé).

#!/bin/bash 

while true 

do 

    ATIME=`stat -c %Z /path/to/the/file.txt` 

    if [[ "$ATIME" != "$LTIME" ]] 

    then 

     echo "RUN COMMNAD" 
     LTIME=$ATIME 
    fi 
    sleep 5 
done 
+4

Il vaut mieux utiliser 'inotifywait' (inotify-tools) car il se réveille presque instantanément lorsque le fichier est mis à jour. (Votre script attendrait jusqu'à 5 secondes avant de le remarquer.) Votre script doit également se réveiller toutes les 5 secondes, générer un processus, vérifier le résultat et ensuite se rendormir, alors que ceci est assez bon pour un "hack it together in 5". minutes "-script, cela gaspille les ressources CPU et devrait être évité dans le code de production. – alexander255

18

-je utiliser ce script pour exécuter un script de compilation sur les changements dans une arborescence de répertoires:

#! /bin/bash 
DIRECTORY_TO_OBSERVE="js"  // might want to change this 
function block_for_change { 
    inotifywait -r \ 
    -e modify,move,create,delete \ 
    $DIRECTORY_TO_OBSERVE 
} 
BUILD_SCRIPT=build.sh   // might want to change this too 
function build { 
    bash $BUILD_SCRIPT 
} 
build 
while block_for_change; do 
    build 
done 

Utilise inotify-tools. Vérifiez inotifywaitman page pour savoir comment personnaliser ce qui déclenche la construction.

+0

Est-ce que cela n'a pas une condition de concurrence? Si vous modifiez un fichier pendant la construction, il ne l'attrapera pas. – FSMaxB

+0

@FSMaxB oui, c'est le cas. Si je devais enregistrer les modifications pendant la construction, j'enregistrerais à nouveau l'un des fichiers source quand il serait fini pour déclencher une nouvelle construction. –

0

Juste à des fins de débogage, quand j'écris un script shell et que vous souhaitez l'exécuter sur Enregistrer, j'utilise ceci:

#!/bin/bash 
file="$1" # Name of file 
command="${*:2}" # Command to run on change (takes rest of line) 
t1="$(ls --full-time $file | awk '{ print $7 }')" # Get latest save time 
while true 
do 
    t2="$(ls --full-time $file | awk '{ print $7 }')" # Compare to new save time 
    if [ "$t1" != "$t2" ];then t1="$t2"; $command; fi # If different, run command 
    sleep 0.5 
done 

Exécuter comme

run_on_save.sh myfile.sh ./myfile.sh arg1 arg2 arg3 

Edit: Au-dessus testé sur Ubuntu 12.04, pour Mac OS, modifiez les lignes ls à:

"$(ls -lT $file | awk '{ print $8 }')" 
0

Ajouter ce qui suit à ~/.bashrc:

function react() { 
    if [ -z "$1" -o -z "$2" ]; then 
     echo "Usage: react <[./]file-to-watch> <[./]action> <to> <take>" 
    elif ! [ -r "$1" ]; then 
     echo "Can't react to $1, permission denied" 
    else 
     TARGET="$1"; shift 
     ACTION="[email protected]" 
     while sleep 1; do 
      ATIME=$(stat -c %Z "$TARGET") 
      if [[ "$ATIME" != "${LTIME:-}" ]]; then 
       LTIME=$ATIME 
       $ACTION 
      fi 
     done 
    fi 
} 
8

Vous pouvez essayer l'outil entr pour exécuter des commandes arbitraires lorsque les fichiers changent. Exemple de fichiers:

$ ls -d * | entr sh -c 'make && make test' 

ou:

$ ls *.css *.html | entr reload-browser Firefox 

Pour les répertoires utilisent -d, mais vous avez à l'utiliser dans la boucle, par exemple:

while true; do find path/ | entr -d echo Changed; done 

ou:

while true; do ls path/* | entr -pd echo Changed; done 
+1

'entr' est l'outil le plus simple, le plus composable et le plus simple pour le travail. Aimer. 'incron' peut être remplacé par' entr' et un gestionnaire de processus comme 'runit',' s6' ou même 'systemd'. – clacke

+1

Ceci. c'est 10 fois plus simple qu'inotifywait – August