627 votes

Comment exécuter une commande à chaque fois qu'un fichier est modifié ?

Je veux un moyen simple et rapide d'exécuter une commande à chaque fois qu'un fichier est modifié. Je veux quelque chose de très simple, quelque chose que je laisserai tourner sur un terminal et que je fermerai dès que j'aurai fini de travailler sur ce fichier.

Actuellement, j'utilise ceci :

while read; do ./myfile.py ; done

Et ensuite je dois aller dans ce terminal et appuyer sur Enter chaque fois que j'enregistre ce fichier sur mon éditeur. Ce que je veux, c'est quelque chose comme ça :

while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done

Ou toute autre solution aussi simple que cela.

BTW : J'utilise Vim, et je sais que je peux ajouter une autocommande pour exécuter quelque chose sur BufWrite, mais ce n'est pas le genre de solution que je veux maintenant.

Mise à jour : Je veux quelque chose de simple, jetable si possible. De plus, je veux quelque chose qui s'exécute dans un terminal car je veux voir la sortie du programme (je veux voir les messages d'erreur).

A propos des réponses : Merci pour toutes vos réponses ! Elles sont toutes très bonnes, et chacune adopte une approche très différente des autres. Comme je ne dois en accepter qu'une seule, j'accepte celle que j'ai effectivement utilisée (elle était simple, rapide et facile à mémoriser), même si je sais qu'elle n'est pas la plus élégante.

0 votes

Possibilité de duplication de site croisé de : stackoverflow.com/questions/2972765/ ( bien qu'ici c'est le sujet =) )

0 votes

J'ai déjà référencé un duplicate cross site et il a été refusé :S ;)

4 votes

La solution de Jonathan Hartley s'appuie sur d'autres solutions proposées ici et corrige les gros problèmes des réponses les plus votées : absence de certaines modifications et inefficacité. Veuillez changer la réponse acceptée par la sienne, qui est également maintenue sur github à l'adresse suivante github.com/tartley/rerun2 (ou à une autre solution sans ces défauts)

0voto

David Ramirez Points 121

L'outil "fido" peut constituer une autre option pour répondre à ce besoin. Voir https://www.joedog.org/fido-home/

0 votes

Veuillez lire Comment recommander un logiciel pour obtenir des conseils sur la manière de recommander des logiciels. Vous devez fournir au moins un lien, des informations supplémentaires sur le logiciel lui-même et la manière dont il peut être utilisé pour résoudre le problème posé par la question.

0voto

petermeissner Points 453

Description

Cela surveillera les changements dans un fichier et exécutera n'importe quelle commande. (y compris d'autres arguments) a été donné comme deuxième déclaration. Elle efface également l'écran et imprime l'heure de la dernière exécution. Remarque : vous pouvez rendre la fonction plus (ou moins) réactive en modifiant le nombre de secondes pendant lesquelles la fonction doit dormir après chaque cycle de la boucle while.

Exemple d'utilisation

watch_file my_file.php php my_file.php

Cette ligne va surveiller un fichier php my_file.php et passer par php l'interprète à chaque fois qu'il change.

Définition de la fonction

function watch_file (){

### Set initial time of file
LTIME=`stat -c %Z $1`
printf "\033c"
echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
${@:2}

while true
do
   ATIME=`stat -c %Z $1`

   if [[ "$ATIME" != "$LTIME" ]]
   then
    printf "\033c"
    echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
    ${@:2}
    LTIME=$ATIME
   fi
   sleep 1
done
}

Crédit

Il s'agit en fait d'une version plus générale de la réponse de VDR.

0voto

Razvan Grigore Points 929

find peut faire l'affaire.

while true; do
    find /path/to/watched/file -ctime 1s | xargs do-what-you-will
done

find -ctime 1s imprime le nom du fichier s'il a été modifié dans le dernier 1s .

0 votes

C'est une bonne idée naïve, mais elle occupera le CPU tout le temps, ne prendra pas en compte les changements si la commande prend plus d'une seconde, exécutera la même commande plusieurs fois si elle prend moins d'une seconde, et échouera si le nom du fichier contient des espaces ou des caractères spéciaux (essayez d'utiliser -exec au lieu de xargs ).

0voto

yunzen Points 127

J'ai connu une situation légèrement différente. Mais je pense que cela peut être utile à quelqu'un qui lit cette question.

J'avais besoin d'être informé lorsqu'un fichier journal changeait de taille, mais pas nécessairement immédiatement. Et comme cela pouvait se produire des jours ou des semaines plus tard, je ne pouvais pas utiliser l'option inotify (qui n'était pas installé/activé sur ce serveur de toute façon) en ligne de commande (je ne voulais pas utiliser la commande nohup ou similaire). J'ai donc décidé d'exécuter un bash script sur cron pour vérifier

Le script écrit la taille du fichier surveillé dans un fichier texte et sur chaque cron exécute et vérifie si cette valeur a changé et m'envoie la dernière ligne si elle a été modifiée.

#!/bin/bash
FILE_TO_WATCH="/path/to/log_file.log"
FILESIZE_FILE="/path_to/record.txt"
SUBJECT="Log file 'log_file.log' has changed"
MAILTO="info@example.com"
BODY="Last line of log file:\n"
LAST_LINES=1

# get old recorded file size from file
OLD_FILESIZE=$(cat "${FILESIZE_FILE}")
# write current file size into file
stat --printf="%s" "${FILE_TO_WATCH}" > "${FILESIZE_FILE}"
# get new recorded file size from file
NEW_FILESIZE=$(cat "${FILESIZE_FILE}")

if [ "${OLD_FILESIZE}" != "${NEW_FILESIZE}" ]; then
    echo -e "${BODY}"$(tail -${LAST_LINES} ${FILE_TO_WATCH}) | mail -s "${SUBJECT}" "${MAILTO}"
fi

0voto

Yeti Points 159

Inotifywait one-liner

Ceci utilise inotifywait tout en évitant l'utilisation de while read -r :

inotifywait -q --format '%f' -e close_write,moved_to -m . |
    grep --line-buffered -F -x 'myfile.py' |
    xargs -l -i './myfile.py'

Explication : inotifywait édite une ligne avec le nom du fichier lorsqu'il détecte un changement, grep filtre le nom du fichier ciblé, et xargs exécute une commande pour chaque nom de fichier.

inotifywait paramètres :

  • -q : Supprimer les messages stderr.
  • --format '%f' N'affiche que le nom du fichier, nous ne filtrons pas les événements de toute façon.
  • -e close_write,moved_to Ne détecte que close_write (le fichier a été écrit), et moved_to (la plupart des éditeurs utilisent des fichiers d'échange, et déplacent la mémoire tampon vers le fichier lors de l'enregistrement).
  • -m Continuer à écouter indéfiniment (appuyer sur CTRL+C pour interrompre).
  • . Répertoire cible qui contient le fichier ciblé.

grep paramètres :

  • --line-buffered : Vider les lignes immédiatement, traiter comme un flux (comme sed ).
  • -F : Nom de fichier littéral, n'analyse pas l'expression régulière (sinon il faudrait échapper le point) : myfile\.py ).
  • -x : Correspondre à la ligne entière, et pas seulement à une sous-chaîne du nom de fichier.

xargs paramètres :

  • -l : Exécuter pour chaque ligne d'entrée, ne pas regrouper les lignes.
  • -i : Empêcher le nom de fichier d'être ajouté comme argument à la commande, il remplace {} dans la commande avec la ligne d'entrée.

Fonction générique pour exécuter une commande lors d'un changement de fichier

Pour un cas plus générique, vous pouvez utiliser cette fonction :

exec-onchange()
{
    local file="$1"
    shift

    # strip path
    local filename="${file##*/}"

    # strip filename
    local path="${file%/*}"
    if [ -z "$path" ]; then path="."; fi

    # catch a custom command
    local cmd="$@"
    local literalFlag=""
    if [ -z "$cmd" ]; then cmd="$path/$filename"; literalFlag="-Fx"; fi

    exec inotifywait -q --format '%f' -e close_write,moved_to -m "$path" |
    grep --line-buffered $literalFlag "$filename" |
    xargs -l -i /bin/bash -c "$cmd"
}

Utilisation :

exec-onchange [file-to-watch] [command-to-execute]

Exemple d'utilisation :

exec-onchange myfile.py

Notez que l'argument est littéral, sauf si une commande personnalisée est ajoutée, auquel cas le premier argument devient une expression régulière. Bien que dans ce cas, les expressions régulières ne soient autorisées que pour la correspondance du nom de fichier, et non du chemin.

Cette fonction permet une utilisation plus complexe. Comme la compilation automatique, tout en exécutant l'exécutable compilé dès que la compilation est terminée (en utilisant un exec-onchange séparé, la compilation peut être faite tout en continuant à exécuter build/main) :

exec-onchange 'src/.*\.cpp' 'echo "{} changed"; gcc src/*.cpp -o build/main' &
exec-onchange build/main

0 votes

Une ligne en trois lignes :) Je l'adore.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X