2010-08-27 20:02:40 +0000 2010-08-27 20:02:40 +0000
459
459

Comment exécuter une commande lorsqu'un fichier change ?

Je veux un moyen rapide et simple d'exécuter une commande lorsqu'un fichier change. Je veux quelque chose de très simple, quelque chose que je laisse tourner sur un terminal et que je ferme lorsque j'ai fini de travailler avec ce fichier.

Actuellement, j'utilise ceci :

while read; do ./myfile.py ; done

Et puis je dois aller sur ce terminal et appuyer sur Entrée, chaque fois que je sauve ce fichier sur mon éditeur. Ce que je veux, c'est quelque chose comme ceci :

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 commande automatique pour exécuter quelque chose sur BufWrite, mais ce n'est pas le genre de solution que je veux maintenant.

Update: Je veux quelque chose de simple, jetable si possible. De plus, je veux que quelque chose fonctionne dans un terminal parce que 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 d'entre elles adopte une approche très différente des autres. Comme je ne dois en accepter qu'une seule, j'accepte celle que j'ai utilisée (elle était simple, rapide et facile à retenir), même si je sais qu'elle n'est pas la plus élégante.

Respuestas (37)

434
434
434
2010-08-27 20:54:55 +0000

Simple, en utilisant inotifywait (installez le paquet inotify-tools de votre distribution) :

while inotifywait -e close_write myfile.py; do ./myfile.py; done

ou

inotifywait -q -m -e close_write myfile.py |
while read -r filename event; do
  ./myfile.py # or "./$filename"
done

Le premier extrait est plus simple, mais il présente un inconvénient important : il ne tiendra pas compte des modifications effectuées pendant que inotifywait ne fonctionne pas (en particulier pendant que myfile fonctionne). Le deuxième extrait ne présente pas ce défaut. Cependant, attention, il suppose que le nom du fichier ne contient pas d'espace. Si c'est un problème, utilisez l'option --format pour modifier la sortie afin de ne pas inclure le nom du fichier :

inotifywait -q -m -e close_write --format %e myfile.py |
while read events; do
  ./myfile.py
done

Dans tous les cas, il y a une limitation : si un programme remplace myfile.py par un fichier différent, au lieu d'écrire dans le myfile existant, inotifywait mourra. De nombreux éditeurs travaillent de cette manière.

Pour surmonter cette limitation, utilisez inotifywait sur le répertoire :

inotifywait -e close_write,moved_to,create -m . |
while read -r directory events filename; do
  if ["$filename" = "myfile.py"]; then
    ./myfile.py
  fi
done

Ou bien utilisez un autre outil qui utilise la même fonctionnalité sous-jacente, comme incron (qui vous permet d'enregistrer des événements lorsqu'un fichier est modifié) ou fswatch (un outil qui fonctionne également sur de nombreuses autres variantes d'Unix, en utilisant l'analogue de chaque variante de Linux inotify).

179
179
179
2013-10-25 09:41:16 +0000

entr http://entrproject.org/ ) fournit une interface plus conviviale pour inotifier (et prend également en charge *BSD & Mac OS X).

Il permet de spécifier très facilement plusieurs fichiers à surveiller (limité uniquement par ulimit -n), évite de s'occuper des fichiers à remplacer et nécessite moins de syntaxe bash :

$ find . -name '*.py' | entr ./myfile.py

Je l'ai utilisé sur toute l'arborescence des sources de mon projet pour effectuer les tests unitaires du code que je suis en train de modifier, et il a déjà considérablement amélioré mon flux de travail. Les drapeaux

comme -c (effacer l'écran entre deux exécutions) et -d (quitter lorsqu'un nouveau fichier est ajouté à un répertoire surveillé) ajoutent encore plus de flexibilité, par exemple vous pouvez faire :

$ while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; done

Début 2018, il est toujours en développement actif et peut être trouvé dans Debian & Ubuntu (apt install entr) ; la construction à partir de la repo de l'auteur était sans douleur dans tous les cas.

112
112
112
2011-06-30 13:34:28 +0000

J'ai écrit un programme Python pour faire exactement cela appelé when-changed .

L'utilisation est simple :

when-changed FILE COMMAND...

Ou pour regarder plusieurs fichiers :

when-changed FILE [FILE ...] -c COMMAND

FILE peut être un répertoire. Regardez récursivement avec -r. Utilisez %f pour passer le nom du fichier à la commande.

53
53
53
2013-08-20 17:12:47 +0000

Et ce scénario ? Il utilise la commande stat pour obtenir le temps 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 consulté).

#!/bin/bash

### Set initial time of file
LTIME=`stat -c %Z /path/to/the/file.txt`

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

   if [["$ATIME" != "$LTIME"]]
   then    
       echo "RUN COMMAND"
       LTIME=$ATIME
   fi
   sleep 5
done
30
30
30
2010-08-27 20:12:25 +0000

Solution utilisant Vim :

:au BufWritePost myfile.py :silent !./myfile.py

Mais je ne veux pas de cette solution parce qu'elle est un peu ennuyeuse à taper, qu'il est un peu difficile de se souvenir de ce qu'il faut taper exactement et qu'il est un peu difficile d'en annuler les effets (nécessité d'exécuter :au! BufWritePost myfile.py). De plus, cette solution bloque Vim jusqu'à ce que la commande ait fini de s'exécuter.

J'ai ajouté cette solution ici juste pour être complet, car elle pourrait aider d'autres personnes.

Pour afficher la sortie du programme (et perturber complètement votre flux d'édition, car la sortie va écraser votre éditeur pendant quelques secondes, jusqu'à ce que vous appuyiez sur Entrée), supprimez la commande :silent.

23
23
23
2012-06-09 23:51:16 +0000

Si vous avez installé npm , nodemon est probablement le moyen le plus simple de démarrer, surtout sous OS X, qui ne dispose apparemment pas d'outils d'inotivation. Il permet d'exécuter une commande lorsqu'un dossier change.

21
21
21
2016-03-22 14:57:03 +0000

Pour ceux qui ne peuvent pas installer inotify-tools comme moi, ceci devrait être utile :

watch -d -t -g ls -lR

Cette commande se termine lorsque la sortie change, ls -lR va lister chaque fichier et répertoire avec sa taille et ses dates, donc si un fichier est modifié, il devrait quitter la commande, comme le dit l'homme :

-g, --chgexit
          Exit when the output of command changes.

Je sais que cette réponse ne sera peut-être lue par personne, mais j'espère que quelqu'un y accédera. Exemple de ligne de commande :

~ $ cd /tmp
~ $ watch -d -t -g ls -lR && echo "1,2,3"

Ouvrir un autre terminal :

~ $ echo "testing" > /tmp/test

Maintenant le premier terminal va sortir : 1,2,3

Exemple de script simple :

#!/bin/bash
DIR_TO_WATCH=${1}
COMMAND=${2}

watch -d -t -g ls -lR ${DIR_TO_WATCH} && ${COMMAND}
17
17
17
2015-09-09 21:49:14 +0000

rerun2 on github ) est un script Bash de 10 lignes de la forme :

#!/usr/bin/env bash

function execute() {
    clear
    echo "$@"
    eval "$@"
}

execute "$@"

inotifywait --quiet --recursive --monitor --event modify --format "%w%f" . \
| while read change; do
    execute "$@"
done

Sauvegardez la version de github comme ‘rerun’ sur votre PATH, et invoquez-le en utilisant :

rerun COMMAND

Il lance COMMAND chaque fois qu'il y a un événement de modification du système de fichiers dans votre répertoire actuel (récursif. )

Ce que l'on pourrait aimer à son sujet :

  • Il utilise inotify, donc est plus réactif que polling. Fabuleux pour exécuter des tests unitaires de l'ordre de la milli-seconde, ou rendre des fichiers graphviz dot, chaque fois que vous appuyez sur “save”.
  • Parce qu'il est si rapide, vous n'avez pas besoin de lui dire d'ignorer les grands sous-répertoires (comme node_modules) juste pour des raisons de performance.
  • Il est super réactif, parce qu'il n'appelle inotifywait qu'une seule fois, au démarrage, au lieu de le lancer, et d'encourir le coûteux coup de l'établissement de montres, à chaque itération.
  • Ce n'est que 12 lignes de Bash
  • Parce que c'est du Bash, il interprète les commandes que vous lui passez exactement comme si vous les aviez tapées à l'invite du Bash. (C'est probablement moins cool si vous utilisez un autre shell).
  • Il ne perd pas les événements qui se produisent pendant l'exécution de COMMAND, contrairement à la plupart des autres solutions d'inotivation de cette page.
  • Sur le premier événement, il entre dans une “période morte” de 0,15 secondes, pendant laquelle les autres événements sont ignorés, avant que COMMAND ne soit exécuté exactement une fois. Cela permet d'éviter que l'avalanche d'événements provoquée par la danse “create-write-move” que Vi ou Emacs effectue lors de la sauvegarde d'un tampon ne provoque de multiples exécutions laborieuses d'une suite de tests qui pourrait être lente. Tous les événements qui se produisent alors que COMMAND est en cours d'exécution ne sont pas ignorés - ils causeront une seconde période d'inactivité et l'exécution ultérieure.

Ce que l'on pourrait ne pas apprécier :

  • Il utilise l'inotivation, donc ne fonctionnera pas en dehors de Linuxland.
  • Parce qu'il utilise l'inotivation, il vomira en essayant de surveiller des répertoires contenant plus de fichiers que le nombre maximum de surveillances d'inotivation de l'utilisateur. Par défaut, il semble être réglé sur environ 5 000 à 8 000 sur les différentes machines que j'utilise, mais il est facile d'augmenter ce nombre. Voir https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Il ne parvient pas à exécuter les commandes contenant des alias Bash. Je pourrais jurer que cela fonctionnait avant. En principe, comme il s'agit de Bash, et non pas d'exécuter une COMMANDE dans une sous-coque, je m'attendrais à ce que cela fonctionne. J'aimerais savoir si quelqu'un sait pourquoi ça ne marche pas. Beaucoup d'autres solutions sur cette page ne peuvent pas non plus exécuter de telles commandes.
  • Personnellement, j'aimerais pouvoir appuyer sur une touche du terminal dans lequel elle s'exécute pour provoquer manuellement une exécution supplémentaire de COMMAND. Pourrais-je ajouter ceci d'une manière ou d'une autre, simplement ? Une boucle “while read -n1” qui s'exécute en même temps et qui appelle aussi execute ?
  • Pour l'instant, je l'ai codée pour effacer le terminal et imprimer la COMMANDE exécutée à chaque itération. Certaines personnes pourraient vouloir ajouter des drapeaux de ligne de commande pour désactiver ce genre de choses, etc. Mais cela augmenterait considérablement la taille et la complexité.

C'est un raffinement de la réponse de @cychoi.

12
12
12
2010-08-27 21:23:29 +0000

Voici un simple script shell Bourne qui :

  1. Prend deux arguments : le fichier à surveiller et une commande (avec des arguments, si nécessaire)
  2. Copie le fichier que vous surveillez dans le répertoire /tmp
  3. Vérifie toutes les deux secondes si le fichier que vous surveillez est plus récent que la copie
  4. S'il est plus récent, il écrase la copie avec l'original plus récent et exécute la commande
  5. Nettoie après lui-même lorsque vous appuyez sur Ctr-C

Cela fonctionne sous FreeBSD. Le seul problème de portabilité auquel je puisse penser est si un autre Unix n'a pas la commande mktemp(1), mais dans ce cas vous pouvez juste coder en dur le nom du fichier temp.

8
8
8
2014-08-08 22:20:09 +0000

si vous avez nodemon installé, alors vous pouvez faire ceci :

nodemon -w <watch directory> -x "<shell command>" -e ".html"

Dans mon cas, je modifie le html localement et l'envoie à mon serveur distant quand un fichier change.

nodemon -w <watch directory> -x "scp filename jaym@jay-remote.com:/var/www" -e ".html"
8
8
8
2010-08-27 20:12:59 +0000

Consultez le site incron . Il est similaire au cron, mais utilise l'inotivation des événements plutôt que le temps.

6
6
6
2014-07-09 13:16:10 +0000

Look into Guard, en particulier avec ce plugin : https://github.com/hawx/guard-shell

Vous pouvez le configurer pour surveiller un nombre quelconque de modèles dans le répertoire de votre projet, et exécuter des commandes lorsque des changements se produisent. Il y a de fortes chances qu'un plugin soit disponible pour ce que vous essayez de faire en premier lieu.

6
6
6
2014-01-22 14:55:46 +0000

Une autre solution avec les NodeJ, * fsmonitor ** :

  1. Install :

  2. Depuis la ligne de commande (exemple, surveiller les journaux et “retail” si un fichier journal change)

5
5
5
2015-12-28 10:44:18 +0000
  • Watchdog ** est un projet Python, et peut être exactement ce que vous cherchez :

Plates-formes supportées

  • Linux 2. 6 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD/BSD (kqueue)
  • Windows (ReadDirectoryChangesW avec les ports de complétion d'E/S ; ReadDirectoryChangesW worker threads)
  • Indépendant du système d'exploitation (interroger le disque pour des instantanés de répertoire et les comparer périodiquement ; lent et non recommandé)

Il suffit d'écrire une enveloppe de ligne de commande pour cela * watchdog_exec ** :

Exemple exécute

Sur un événement fs impliquant des fichiers et des dossiers dans le répertoire courant, exécutez la commande echo $src $dst, sauf si l'événement fs est modifié, puis exécutez la commande python $src.

python -m watchdog_exec . --execute echo --modified python

Utilisation d'arguments courts, et restriction à l'exécution uniquement lorsque les événements impliquent “ main.py” :

python -m watchdog_exec . -e echo -a echo -s __main__.py

EDIT : Watchdog vient de trouver un CLI officiel appelé watchmedo, alors vérifiez aussi.

5
5
5
2012-07-02 16:36:51 +0000

Sous Linux :

man watch

watch -n 2 your_command_to_run

Exécutera la commande toutes les 2 secondes.

Si votre commande prend plus de 2 secondes à s'exécuter, watch attendra qu'elle soit terminée avant de la refaire.

4
4
4
2010-08-27 20:37:52 +0000

Si votre programme génère une sorte de log/output, vous pouvez créer un Makefile avec une règle pour ce log/output qui dépend de votre script et faire quelque chose comme

while true; do make -s my_target; sleep 1; done

Alternativement, vous pouvez créer une fausse cible et avoir la règle pour cela à la fois appeler votre script et toucher la fausse cible (tout en dépendant toujours de votre script).

4
4
4
2014-09-15 23:24:58 +0000
4
4
4
2015-06-17 18:03:51 +0000

Amélioré par réponse de Gilles .

Cette version exécute inotifywait une fois et surveille les événements ( par exemple : modify) par la suite. De telle sorte que inotifywait _n'a pas besoin d'être ré-exécuté à chaque événement rencontré.

C'est rapide et rapide ! (même en surveillant un grand répertoire de manière récursive)_

inotifywait --quiet --monitor --event modify FILE | while read; do
    # trim the trailing space from inotifywait output
    REPLY=${REPLY% }
    filename=${REPLY%% *}
    # do whatever you want with the $filename
done
3
3
3
2010-08-27 20:05:59 +0000

Un peu plus sur le plan de la programmation, mais vous voulez quelque chose comme inotify . Il existe des implémentations dans de nombreux langages, tels que jnotify et pyinotify .

Cette bibliothèque vous permet de surveiller des fichiers individuels ou des répertoires entiers, et renvoie des événements lorsqu'une action est découverte. Les informations renvoyées comprennent le nom du fichier, l'action (création, modification, renommage, suppression) et le chemin d'accès au fichier, entre autres informations utiles.

3
3
3
2012-11-11 21:48:31 +0000

Pour ceux d'entre vous qui cherchent une solution FreeBSD, voici le port :

/usr/ports/sysutils/wait_on
3
3
3
2014-04-29 13:56:13 +0000

J'aime la simplicité de while inotifywait ...; do ...; done mais il y a deux problèmes :

  • Les changements de fichiers qui se produisent pendant le do ...; vont être manqués
  • Lent lors de l'utilisation en mode récursif

C'est pourquoi j'ai fait un script d'aide qui utilise inotifywait sans ces limitations : inotifyexec

Je vous suggère de mettre ce script sur votre chemin, comme dans ~/bin/. L'utilisation est décrite en exécutant simplement la commande.

Exemple : inotifyexec "echo test" -r .

3
3
3
2016-04-12 14:53:44 +0000

Amélioré solution de Sebastian avec la commande watch :

watch_cmd.sh :

#!/bin/bash
WATCH_COMMAND=${1}
COMMAND=${2}

while true; do
  watch -d -g "${WATCH_COMMAND}"
  ${COMMAND}
  sleep 1 # to allow break script by Ctrl+c
done

Exemple d'appel :

watch_cmd.sh "ls -lR /etc/nginx | grep .conf$" "sudo service nginx reload"

Cela fonctionne mais attention : la commande watch a des bugs connus (voir man) : elle réagit sur les changements uniquement en VISIBLE dans les parties terminales de la sortie de -g CMD.

2
2
2
2017-03-01 20:14:14 +0000

Vous pouvez essayer reflex .

Reflex est un petit outil qui permet de surveiller un répertoire et de relancer une commande lorsque certains fichiers changent. Il est idéal pour exécuter automatiquement des tâches de compilation/lien/test et pour recharger votre application lorsque le code change.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make
1
1
1
2017-04-05 07:38:43 +0000

J'ai une GIST pour cela et l'utilisation est assez simple

watchfiles <cmd> <paths...>

https://gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55

1
1
1
2017-06-03 12:10:57 +0000

Comme quelques autres l'ont fait, j'ai également écrit un outil de ligne de commande léger pour ce faire. Il est entièrement documenté, testé et modulaire. Vous pouvez l'installer (si vous avez Python3 et pip) en utilisant :

pip3 install git+https://github.com/vimist/watch-do

Usage

Utilisation

Utilisez-le immédiatement en l'exécutant :

watch-do -w my_file -d 'echo %f changed'

Vue d'ensemble des fonctionnalités

  • Prise en charge de l'agrégation globale de fichiers (utilisez -w '*.py' ou -w '**/*.py')
  • Exécutez plusieurs commandes lors d'un changement de fichier (spécifiez simplement le drapeau -d à nouveau)
  • Maintient dynamiquement la liste des fichiers à surveiller si l'agrégation globale est utilisée (-r pour l'activer)
  • Plusieurs façons de “surveiller” un fichier :
  • Temps de modification (par défaut)
  • Hachage de fichier
  • Trivial pour implémenter le vôtre (c'est le ModificationTime watcher)
  • Conception modulaire. Si vous voulez faire exécuter des commandes, lorsqu'un fichier est consulté, il est trivial d'écrire votre propre watcher (mécanisme qui détermine si les faiseurs doivent être exécutés).
1
1
1
2016-02-26 19:10:05 +0000

J'utilise ce scénario pour le faire. J'utilise inotify en mode moniteur

#!/bin/bash
MONDIR=$(dirname $1)
ARQ=$(basename $1)

inotifywait -mr -e close_write $MONDIR | while read base event file 
do
  if (echo $file |grep -i "$ARQ") ; then
    $1
  fi
done

Enregistrez ceci sous runatwrite.sh

Usage: runatwrite.sh myfile.sh

il lancera myfile.sh à chaque écriture.

1
1
1
2015-09-09 19:36:04 +0000

J'ai écrit un programme Python pour faire exactement cela, appelé rerun .

UPDATE : Cette réponse est un script Python qui interroge les changements, ce qui est utile dans certaines circonstances. Pour un script Bash sous Linux uniquement qui utilise inotify, voir mon autre réponse, cherchez sur cette page “rerun2”.

Installez Python2 ou Python3 avec :

pip install --user rerun

et l'utilisation est très simple :

rerun "COMMAND"

La commande est attendue comme un seul arg, pas une séquence d'args séparés par des espaces. Citez-la donc comme indiqué, ce qui réduit le nombre d'échappements supplémentaires que vous devriez ajouter. Il suffit de taper la commande comme vous l'auriez fait en ligne de commande, mais entre guillemets.

Par défaut, il surveille tous les fichiers dans ou sous le répertoire courant, en sautant des choses comme les répertoires de contrôle de source connus, .git, .svn, etc.

Les options comprennent “-i NAME” qui ignore les modifications apportées aux fichiers ou répertoires nommés. Il peut être donné plusieurs fois.

Comme il s'agit d'un script Python, il doit exécuter la commande comme un sous-processus, et nous utilisons une nouvelle instance du shell actuel de l'utilisateur pour interpréter “COMMAND” et décider du processus à exécuter. Cependant, si votre commande contient des alias de shell et autres qui sont définis dans .bashrc, ceux-ci ne seront pas chargés par le sous-système. Pour remédier à ce problème, vous pouvez donner un drapeau “-I” à la relance, pour utiliser des sous-coques interactifs (alias “login”). C'est plus lent et plus sujet aux erreurs que de lancer un shell normal, car il doit fournir le source de votre .bashrc.

Je l'utilise avec Python 3, mais la dernière fois que j'ai vérifié, la rediffusion fonctionnait toujours avec Python 2.

L'épée à double tranchant est qu'elle utilise le polling au lieu de l'inotify. D'un autre côté, cela signifie qu'il fonctionne sur tous les OS. De plus, il est meilleur que certaines autres solutions présentées ici, car il n'exécute la commande qu'une seule fois pour un ensemble de modifications du système de fichiers, et non pas une fois par fichier modifié, alors qu'en même temps, il exécute la commande une seconde fois si un fichier change à nouveau pendant l'exécution de la commande.

D'un autre côté, l'interrogation signifie qu'il y a une latence de 0,0 à 1,0 seconde, et bien sûr, il est lent à surveiller des répertoires extrêmement importants. Cela dit, je n'ai jamais rencontré de projet suffisamment important pour que cela se remarque, tant que vous utilisez “-i” pour ignorer les gros problèmes comme vos modules virtualenv et node_.

Hmmm. rerun m'est indispensable depuis des années - je l'utilise en gros huit heures par jour pour effectuer des tests, reconstruire des fichiers .dot au fur et à mesure que je les modifie, etc. Mais maintenant que j'en arrive à taper ça ici, il est clair que je dois passer à une solution qui utilise inotify (je n'utilise plus Windows ou OSX.) et qui est écrite en Bash (donc ça fonctionne avec des alias sans aucun bidouillage supplémentaire.)

1
1
1
2015-09-01 04:53:52 +0000

Une réponse en ligne que j'utilise pour suivre un changement de fichier :

$ while true ; do NX=`stat -c %Z file` ; [[$BF != $NX]] && date >> ~/tmp/fchg && BF=$NX || sleep 2 ; done

Vous n'avez pas besoin d'initialiser BF si vous savez que la première date est l'heure de début.

C'est simple et portable. Il y a une autre réponse basée sur la même stratégie utilisant un script ici. Regardez aussi.


Utilisation : J'utilise ceci pour déboguer et garder un oeil sur ~/.kde/share/config/plasma-desktop-appletsrc ; qui pour une raison inconnue continue de perdre mon SwitchTabsOnHover=false

1
1
1
2016-03-22 15:33:56 +0000

Pour ceux qui utilisent OS X, vous pouvez utiliser un LaunchAgent pour surveiller un chemin/fichier pour les changements et faire quelque chose quand cela se produit. Pour votre information - LaunchControl est une bonne application pour créer/modifier/supprimer facilement des démons et des agents.

exemple tiré d'ici )

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>test</string>
    <key>ProgramArguments</key>
    <array>
        <string>say</string>
        <string>yy</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>~/Desktop/</string>
    </array>
</dict>
</plist>
0
0
0
2019-11-26 15:53:22 +0000

Si vous ne voulez rien installer de nouveau pour cela, voici un petit script shell que vous pouvez mettre dans votre chemin (par exemple sous $HOME/bin). Il lance une commande lorsque le ou les fichiers fournis sont modifiés. Par exemple :

$ onchange './build' *.txt
#!/bin/sh
cmd="$1"; shift
files="$@"
changed() { tar -c $files | md5sum; } # for on every save use: `stat -c %Z $files`
while true; do
  if ["$(changed)" != "$last"]; then
    last="$(changed)"
    $cmd
  fi
  sleep 1
done

Il met en goudron, puis hachète le contenu des fichiers et/ou des répertoires, de sorte qu'il ne s'exécute pas chaque fois que vous appuyez compulsivement sur CTRL-S (ou tapez :w), mais seulement lorsque quelque chose change réellement. Notez qu'il vérifiera chaque seconde, alors n'en incluez pas trop ou votre machine pourrait devenir lente. Si vous voulez qu'il fonctionne à chaque sauvegarde, utilisez stat à la place (voir le commentaire). Aussi, pour mac, md5sum s'appelle md5 si je me souviens bien.

Petit truc sympa : au moment où vous voulez l'utiliser, vous voudrez probablement répéter la dernière commande que vous venez d'exécuter, mais encore et encore. Vous pouvez utiliser le raccourci !! pour “injecter” la dernière commande dans celle-ci :

$ onchange "!!" *.txt
0
0
0
2018-04-12 18:32:28 +0000

Description

Ceci surveillera un fichier pour les changements et exécutera toute commande (y compris les arguments supplémentaires) qui a été donnée comme deuxième instruction. Il effacera également l'écran et imprimera l'heure de la dernière exécution. Note : vous pouvez rendre la fonction plus (ou moins) réactive en changeant le nombre de secondes pendant lesquelles la fonction doit se mettre en veille après chaque cycle de boucle de temps.

Exemple d'utilisation

watch_file my_file.php php my_file.php

Cette ligne surveillera un fichier php my_file.php et passera par l'interpréteur php chaque fois qu'il change.

Définition de la fonction

function watch_file (){

### Set initial time of file
LTIME=`stat -c %Z $1`
printf "&00133c"
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 "&00133c"
    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 essentiellement d'une version plus générale de la réponse du VDR.

0
0
0
2018-03-19 20:30:21 +0000

Utilisation de base

Voici une solution qui ne nécessite pas l'installation d'un autre logiciel et qui fonctionne tout seul.

tail -q --follow=name myfile.txt | head -n 0

Cette commande se termine dans les conditions suivantes :

  • Une ligne est ajoutée à myfile.txt après l'exécution de la commande
  • Le myfile.txt est remplacé par un autre après l'exécution de la commande

Vous dites que vous utilisez vim, et vim remplacera le fichier lors de la sauvegarde. J'ai testé que cela fonctionne avec vim.

Vous pouvez ignorer la sortie de cette commande, elle peut mentionner quelque chose comme :

tail : monfichier.txt’ a été remplacé ; à la fin du nouveau fichier

Utilisation avancée

Vous pouvez combiner cette commande avec timeout pour retourner vrai ou faux. Vous pouvez l'utiliser comme ceci :

timeout 5s bash -c ‘tail -q –follow=name pipe 2> /dev/null | head -n 0’ && echo changed || echo timeout

Discussion

tail utilise inotify sous le capot. C'est comme ça qu'on obtient ce comportement asynchrone fantaisiste sans aucun sondage. Il y a probablement un autre programme unix standard qui utilise inotify dont nous pouvons abuser de manière plus élégante.

Parfois ces commandes se terminent immédiatement mais si vous les exécutez immédiatement une seconde fois alors elles fonctionnent comme annoncé. J'ai fait une erreur “off-by-one” quelque part, aidez-moi à la corriger.

Sur RHEL, je peux utiliser :

timeout 5s sh -c ‘gio monitor pipe | head -n 0’ && echo changed || echo timeout

Mais je ne suis pas sûr que ce soit portable.

0
0
0
2019-01-28 16:51:42 +0000

Le find peut faire l'affaire. Le

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é changé dans le dernier 1s.

0
0
0
2015-05-13 15:21:33 +0000

Pour les personnes qui trouvent ceci en cherchant sur Google des modifications à un fichier particulier, la réponse est beaucoup plus simple (inspirée par la réponse de Gilles ).

Si vous voulez faire quelque chose après qu'un fichier particulier ait été écrit, voici comment :

while true; do
  inotifywait -e modify /path/to/file
  # Do something *after* a write occurs, e.g. copy the file
  /bin/cp /path/to/file /new/path
done

Enregistrez ceci sous, par exemple, copy_myfile.sh et mettez le fichier .sh dans le dossier /etc/init.d/ pour le faire fonctionner au démarrage.

0
0
0
2019-03-21 11:33:37 +0000

J'ai eu 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 averti lorsqu'un fichier journal changeait de taille, mais ce n'était pas nécessaire immédiatement. Et cela pouvait être des jours ou des semaines dans le futur, donc je ne pouvais pas utiliser inotify (qui n'était pas installé/activé sur ce serveur de toute façon) en ligne de commande (je ne voulais pas utiliser nohup ou similaire). J'ai donc décidé d'exécuter un script bash sur cron pour vérifier

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

#!/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
0
0
0
2019-11-23 23:41:53 +0000

Consultez https://github.com/watchexec/watchexec .

watchexec est un outil simple et autonome qui surveille un chemin et exécute une commande chaque fois qu'il détecte des modifications.

Exemple

Surveillez tous les fichiers JavaScript, CSS et HTML dans le répertoire courant et tous les sous-répertoires pour les modifications, en exécutant make lorsqu'une modification est détectée :

$ watchexec --exts js,css,html make

0
0
0
2017-03-20 13:52:53 +0000

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