Usando inotifywait junto con vim

14

Tengo un script simple que monitorea los cambios en el archivo y lo sincroniza con una copia remota:

#!/bin/bash

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

Funciona bien con nano, pero falla con vim. Cuando uso nano, genera:

somefile CLOSE_WRITE,CLOSE   

y comienza el siguiente ciclo esperando otra edición.

Cuando uso vim, no hay salida, el script solo se cierra con el código de salida 0.

Investigué un poco y descubrí que close_write es el parámetro correcto para usar initofywait junto con vim (primero quería usar el evento de modificación), pero por alguna razón falla para mí.

adam767667
fuente
Esto funciona para mi. ¿ Cambiaste el archivo en vim antes de guardarlo, en lugar de solo abrirlo para editarlo?
roaima
@roaima Solo funciona si la backupcopyopción está desactivada.
Gilles 'SO- deja de ser malvado'

Respuestas:

15

Los editores pueden seguir varias estrategias para guardar un archivo. Las dos variantes principales son sobrescribir el archivo existente o escribir en un archivo nuevo y moverlo en su lugar. Escribir en un archivo nuevo y moverlo en su lugar tiene la buena propiedad de que, en cualquier momento, la lectura del archivo le brinda una versión completa del archivo (un instante el anterior, el siguiente al nuevo). Si el archivo se sobrescribe en su lugar, hay un tiempo durante el cual está incompleto, lo cual es problemático si algún otro programa accede a él en ese momento o si el sistema falla.

Nano aparentemente sobrescribe el archivo existente. Su script detecta el punto cuando termina de escribir (el close_writeevento) y se ejecuta rsyncen ese punto. Tenga en cuenta que es posible que rsync obtenga una versión incompleta del archivo, si guarda dos veces seguidas, antes de que rsync haya completado su trabajo desde el primer guardado.

Vim, por otro lado, utiliza la estrategia de escribir y luego mover, algo en el sentido de

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

Lo que sucede con la versión anterior del archivo es que se elimina en el punto donde la nueva versión se coloca en su lugar. En este punto, el inotifywaitcomando regresa porque el archivo que se le ha dicho que vigile ya no existe. (El nuevo somefilees un archivo diferente con el mismo nombre). Si Vim se hubiera configurado para hacer un archivo de copia de seguridad, lo que sucedería es algo como

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

y inotifywaitahora estaría mirando la copia de seguridad.

Para obtener más información sobre las estrategias de guardado de archivos, consulte ¿Cómo es posible hacer una actualización en vivo mientras se ejecuta un programa? y permisos de archivo y guardar

Se le puede decir a Vim que use la estrategia de sobrescritura: desactive la backupcopyopción ( :set nobackupcopy). Esto es arriesgado, como se indicó anteriormente.

Para manejar ambas estrategias de guardado, mire el directorio y filtre los eventos close_writey los moved_toeventos 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- deja de ser malvado'
fuente
El inconveniente del método propuesto aquí es que cuando escribe varios archivos "a la vez", sus comandos se ejecutarán una vez para cada archivo. Estoy editando código, por lo que puedo modificar un encabezado y un par de otras unidades de traducción y :wa. Luego, mi compilación se ejecuta una vez por cada archivo escrito.
Expiación limitada
@LimitedAtonement Ese es un caso de uso mucho más complicado que el de esta pregunta. Para su caso de uso, deberá esperar un poco después de guardar un archivo. Los archivos nunca se modifican realmente "a la vez". Si guarda varios archivos con :wa, obtendrá sucesivos eventos de inotify. Tendría que esperar después del primero para ver si vienen otros. Pero puede usar el código presentado aquí: la complejidad adicional iría dentro del .
Gilles 'SO- deja de ser malo'
@Gilles decidí más while true; do inotifywait ... [no -m]; make; sleep .1; done;o menos. Hay algunas trampas, pero he llegado a algo bastante viable.
Expiación limitada