Utiliser inotifywait avec vim

14

J'ai un script simple qui surveille les modifications du fichier et le synchronise avec une copie à distance:

#!/bin/bash

while inotifywait -e close_write somefile
do
    rsync somefile [email protected]:./somefile
done

Cela fonctionne très bien avec nano, mais échoue avec vim. Lorsque j'utilise nano, il génère:

somefile CLOSE_WRITE,CLOSE   

et démarre la boucle suivante en attendant une autre édition.

Lorsque j'utilise vim, il n'y a pas de sortie, le script se ferme simplement avec le code de sortie 0.

J'ai fait quelques recherches et j'ai découvert que close_write est le bon paramètre pour utiliser initofywait avec vim (d'abord, je voulais utiliser l'événement de modification), mais pour une raison quelconque, cela échoue pour moi.

adam767667
la source
Ça marche pour moi. Vous avez changé le fichier dans vim avant de l'enregistrer, plutôt que de simplement l'ouvrir pour le modifier?
roaima
@roaima Cela ne fonctionne que si l' backupcopyoption est désactivée.
Gilles 'SO- arrête d'être méchant'

Réponses:

15

Les éditeurs peuvent suivre plusieurs stratégies pour enregistrer un fichier. Les deux variantes principales consistent à écraser le fichier existant ou à écrire dans un nouveau fichier et à le déplacer en place. Écrire dans un nouveau fichier et le déplacer en place a la belle propriété qu'à tout moment, la lecture du fichier vous donne une version complète du fichier (un instant l'ancien, l'instant suivant le nouveau). Si le fichier est écrasé sur place, il y a un temps pendant lequel il est incomplet, ce qui est problématique si un autre programme y accède à ce moment ou si le système plante.

Nano écrase apparemment le fichier existant. Votre script détecte le moment où il a fini d'écrire (l' close_writeévénement) et s'exécute rsyncà ce stade. Notez qu'il est possible pour rsync de récupérer une version incomplète du fichier, si vous enregistrez deux fois de suite rapidement, avant que rsync n'ait terminé son travail à partir de la première sauvegarde.

Vim, d'autre part, utilise la stratégie d'écriture puis de déplacement - quelque chose qui a pour effet de

echo 'new content' >somefile.new
mv -f somefile.new somefile

Ce qui arrive à l'ancienne version du fichier, c'est qu'il est supprimé au point où la nouvelle version est déplacée en place. À ce stade, la inotifywaitcommande revient, car le fichier qu'il a été invité à surveiller n'existe plus. (Le nouveau somefileest un fichier différent portant le même nom.) Si Vim avait été configuré pour créer un fichier de sauvegarde, ce qui se passerait serait quelque chose comme

echo 'new content' >somefile.new
ln somefile somefile.old
mv -f somefile.new somefile

et inotifywaitserait maintenant en train de regarder la sauvegarde.

Pour plus d'informations sur les stratégies d'enregistrement de fichiers, voir Comment est-il possible de faire une mise à jour en direct pendant l'exécution d'un programme? et autorisations de fichiers et enregistrement

On peut dire à Vim d'utiliser la stratégie de remplacement: désactivez l' backupcopyoption ( :set nobackupcopy). C'est risqué, comme indiqué ci-dessus.

Pour gérer les deux stratégies de sauvegarde, regardez le répertoire et filtrez les deux close_writeet les moved_toévénements pour somefile.

inotifywait -m -e close_write,moved_to --format %e/%f . |
while IFS=/ read -r events file; do
  if [ "$file" = "somefile" ]; then
    …
  fi
done
Gilles 'SO- arrête d'être méchant'
la source
L'inconvénient de la méthode proposée ici est que lorsque vous écrivez plusieurs fichiers "à la fois", vos commandes seront exécutées une fois pour chaque fichier. J'édite du code, je peux donc modifier un en-tête et quelques autres unités de traduction et :wa. Ensuite, ma build est exécutée une fois pour chaque fichier écrit.
Expiation limitée du
@LimitedAtonement C'est un cas d'utilisation beaucoup plus compliqué que celui de cette question. Pour votre cas d'utilisation, vous devrez attendre un peu après l'enregistrement d'un fichier. Les fichiers ne sont jamais vraiment modifiés «à la fois». Si vous enregistrez plusieurs fichiers avec :wa, vous obtenez des événements inotify successifs. Vous devrez attendre après le premier pour voir si d'autres arrivent. Mais vous pouvez utiliser le code présenté ici: la complexité supplémentaire irait à l'intérieur du .
Gilles 'SO- arrête d'être méchant'
@Gilles, j'ai décidé while true; do inotifywait ... [no -m]; make; sleep .1; done;ou non. Il y a quelques pièges, mais je suis arrivé à quelque chose de tout à fait réalisable.
Expiation limitée du