Quiero una forma rápida y sencilla de ejecutar un comando cada vez que cambie un archivo. Quiero algo muy simple, algo que dejaré ejecutándose en un terminal y lo cerraré cada vez que termine de trabajar con ese archivo.
Actualmente, estoy usando esto:
while read; do ./myfile.py ; done
Y luego tengo que ir a esa terminal y presionar Enter, cada vez que guardo ese archivo en mi editor. Lo que quiero es algo como esto:
while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done
O cualquier otra solución tan fácil como eso.
Por cierto: estoy usando Vim, y sé que puedo agregar un autocomando para ejecutar algo en BufWrite, pero este no es el tipo de solución que quiero ahora.
Actualización: quiero algo simple, descartable si es posible. Además, quiero que algo se ejecute en un terminal porque quiero ver la salida del programa (quiero ver mensajes de error).
Sobre las respuestas: ¡ Gracias por todas sus respuestas! Todos ellos son muy buenos, y cada uno adopta un enfoque muy diferente de los demás. Como necesito aceptar solo uno, estoy aceptando el que realmente he usado (fue simple, rápido y fácil de recordar), aunque sé que no es el más elegante.

Respuestas:
Simple, usando inotifywait (instale el
inotify-toolspaquete de su distribución ):o
El primer fragmento es más simple, pero tiene una desventaja significativa: perderá los cambios realizados mientras
inotifywaitno se está ejecutando (en particular mientras semyfileestá ejecutando). El segundo fragmento no tiene este defecto. Sin embargo, tenga en cuenta que supone que el nombre del archivo no contiene espacios en blanco. Si eso es un problema, use la--formatopción para cambiar la salida para no incluir el nombre del archivo:De cualquier manera, hay una limitación: si algún programa reemplaza
myfile.pycon un archivo diferente, en lugar de escribir en el existentemyfile,inotifywaitmorirá. Muchos editores trabajan de esa manera.Para superar esta limitación, use
inotifywaiten el directorio:Alternativamente, use otra herramienta que use la misma funcionalidad subyacente, como incron (le permite registrar eventos cuando se modifica un archivo) o fswatch (una herramienta que también funciona en muchas otras variantes de Unix, usando el análogo de cada variante de inotify de Linux).
fuente
sleep_until_modified.shscript fácil de usar , disponible en: bitbucket.org/denilsonsa/small_scripts/srcwhile sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; donees fantastico. Gracias.inotifywait -e delete_selfParece que funciona bien para mí.while inotifywait -e close_write myfile.py; do ./myfile.py; donesiempre sale sin ejecutar el comando (bash y zsh). Para que esto funcione, necesitaba agregar|| true, por ejemplo:while inotifywait -e close_write myfile.py || true; do ./myfile.py; doneentr ( http://entrproject.org/ ) proporciona una interfaz más amigable para inotificar (y también es compatible con * BSD y Mac OS X).
Hace que sea muy fácil especificar varios archivos para ver (limitado solo por
ulimit -n), elimina la molestia de tratar con los archivos que se reemplazan y requiere menos sintaxis bash:Lo he estado usando en todo el árbol fuente de mi proyecto para ejecutar las pruebas unitarias del código que estoy modificando actualmente, y ya ha sido un gran impulso para mi flujo de trabajo.
Las banderas como
-c(borrar la pantalla entre ejecuciones) y-d(salir cuando se agrega un nuevo archivo a un directorio monitoreado) agregan aún más flexibilidad, por ejemplo, puede hacer:A principios de 2018 todavía está en desarrollo activo y se puede encontrar en Debian y Ubuntu (
apt install entr); construir desde el repositorio del autor fue sin dolor en cualquier caso.fuente
-dbandera; es un poco más largo, pero puede hacerwhile sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; donefrente a los nuevos archivos.brew install entrlo que funcionará como se esperabaEscribí un programa de Python para hacer exactamente esto llamado cuándo se cambió .
El uso es simple:
O para ver múltiples archivos:
FILEpuede ser un directorio Mira recursivamente con-r. Use%fpara pasar el nombre del archivo al comando.fuente
when-changed FILE 'clear; COMMAND'.when-changedahora es multiplataforma! Echa un vistazo a la última versión 0.3.0 :)¿Qué tal este guión? Utiliza el
statcomando para obtener el tiempo de acceso de un archivo y ejecuta un comando cada vez que hay un cambio en el tiempo de acceso (cada vez que se accede al archivo).fuente
statsería mejor responder la hora modificada cuando el archivo cambia?vmstat -dpara ver si hay acceso al disco. Parece que Linux hace un trabajo fantástico al almacenar en caché este tipo de cosas: DSolución usando Vim:
Pero no quiero esta solución porque es un poco molesto escribir, es un poco difícil recordar qué escribir, exactamente, y es un poco difícil deshacer sus efectos (es necesario ejecutar
:au! BufWritePost myfile.py). Además, esta solución bloquea Vim hasta que el comando haya terminado de ejecutarse.He agregado esta solución aquí solo para completar, ya que podría ayudar a otras personas.
Para mostrar la salida del programa (e interrumpir completamente su flujo de edición, ya que la salida sobrescribirá su editor durante unos segundos, hasta que presione Entrar), elimine el
:silentcomando.fuente
entr(ver más abajo) - simplemente hacer vim toca un archivo ficticio que entr está mirando, y dejar que haga el resto entr en el fondo ... otmux send-keyssi le toca estar en un ambiente así :).vimrcarchivoSi tiene
npminstalado,nodemones probablemente la forma más fácil de comenzar, especialmente en OS X, que aparentemente no tiene herramientas de inotify. Admite ejecutar un comando cuando cambia una carpeta.fuente
nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/modelsWatchPathsclave como se muestra en mi enlace.Para aquellos que no pueden instalar
inotify-toolscomo yo, esto debería ser útil:watch -d -t -g ls -lREste comando saldrá cuando cambie la salida,
ls -lRenumerará cada archivo y directorio con su tamaño y fechas, por lo que si se cambia un archivo, debería salir del comando, como dice man:Sé que esta respuesta no puede ser leída por nadie, pero espero que alguien llegue a ella.
Ejemplo de línea de comando:
Abre otra terminal:
Ahora saldrá el primer terminal
1,2,3Ejemplo de script simple:
fuente
watch -d -t -g "ls -lR tmp | sha1sum"watch from procps-ng 3.3.10) acepta segundos flotantes para su intervalo, porwatch -n0.2 ...lo tanto , sondeará cada quinto de segundo. Bueno para esas pruebas unitarias saludables de menos de milisegundos.rerun2( en github ) es un script Bash de 10 líneas de la forma:Guarde la versión de github como 'volver a ejecutar' en su RUTA e invoque usando:
Ejecuta COMMAND cada vez que hay un evento de modificación del sistema de archivos dentro de su directorio actual (recursivo).
Cosas que a uno le pueden gustar:
Cosas que a uno no le gustarían:
Este es un refinamiento de la respuesta de @ cychoi.
fuente
"$@"lugar de$@, para trabajar correctamente con argumentos que contienen espacios. Pero al mismo tiempo que usaeval, lo que obliga al usuario de la repetición a tener mucho cuidado al citar.rerun 'command'. ¿Está diciendo que si usé "$ @", el usuario podría invocar comorerun command(sin comillas?) Eso no me parece útil: generalmente no quiero que Bash procese ningún comando antes de pasarlo volver a ejecutar. por ejemplo, si el comando contiene "echo $ myvar", querré ver los nuevos valores de myvar en cada iteración.rerun foo "Some File"podría romperse. Pero como lo está utilizandoeval, puede reescribirse comorerun 'foo "Some File". Tenga en cuenta que a veces la expansión de la ruta puede introducir espacios:rerun touch *.fooes probable que se rompa, y el usorerun 'touch *.foo'tiene una semántica ligeramente diferente (la expansión de la ruta ocurre solo una vez o varias veces).rerun ls "some file"rompe debido a los espacios.rerun touch *.foo*generalmente funciona bien, pero falla si los nombres de archivo que coinciden con * .foo contienen espacios. Gracias por ayudarme a ver cómorerun 'touch *.foo'tiene una semántica diferente, pero sospecho que la versión con comillas simples es la semántica que quiero: quiero que cada iteración de la repetición actúe como si volviera a escribir el comando, por lo tanto, quiero*.fooexpandirme en cada iteración . Probaré sus sugerencias para examinar sus efectos ...Aquí hay un simple script de shell Bourne que:
Se limpia después de sí mismo cuando presiona Ctr-C
Esto funciona en FreeBSD. El único problema de portabilidad en el que puedo pensar es si algún otro Unix no tiene el comando mktemp (1), pero en ese caso simplemente puede codificar el nombre del archivo temporal.
fuente
$cmd, pero afortunadamente eso es fácilmente reparable: deshaga lacmdvariable y ejecute"$@". Su secuencia de comandos no es adecuada para monitorear un archivo grande, pero eso podría solucionarse reemplazandocpportouch -r(solo necesita la fecha, no el contenido). En cuanto a la portabilidad, la-ntprueba requiere bash, ksh o zsh.Echa un vistazo a incron . Es similar a cron, pero usa eventos inotify en lugar de tiempo.
fuente
Otra solución con NodeJs, fsmonitor :
Instalar en pc
Desde la línea de comando (ejemplo, supervisar registros y "venta minorista" si un archivo de registro cambia)
fuente
tail -F -q *.logcreo que el ejemplo podría resolverse .tail -fnoclearla terminal.Mira en Guard, en particular con este complemento:
https://github.com/hawx/guard-shell
Puede configurarlo para ver cualquier número de patrones en el directorio de su proyecto y ejecutar comandos cuando ocurran cambios. Buena posibilidad incluso de que haya un complemento disponible para eso que estás tratando de hacer en primer lugar.
fuente
si tiene instalado nodemon , puede hacer esto:
En mi caso, edito html localmente y lo envío a mi servidor remoto cuando cambia un archivo.
fuente
Bajo Linux:
Ejecutará el comando cada 2 segundos.
Si su comando tarda más de 2 segundos en ejecutarse, el reloj esperará hasta que esté listo antes de hacerlo nuevamente.
fuente
Watchdog es un proyecto de Python, y puede ser justo lo que estás buscando:
Acabo de escribir un contenedor de línea de comandos para ello
watchdog_exec:Ejecuciones de ejemplo
En el evento fs que involucra archivos y carpetas en el directorio actual, ejecute el
echo $src $dstcomando, a menos que se modifique el evento fs, luego ejecute elpython $srccomando.Usando argumentos cortos y restringiendo la ejecución solo cuando los eventos involucran " main .py":
EDITAR: Acabo de encontrar que Watchdog tiene una CLI oficial llamada
watchmedo, así que échale un vistazo también.fuente
Si su programa genera algún tipo de registro / salida, puede crear un Makefile con una regla para ese registro / salida que depende de su script y hacer algo como
Alternativamente, puede crear un objetivo falso y tener la regla para que llame a su script y toque el objetivo falso (aunque aún depende de su script).
fuente
while sleep 1 ; do something ; donees un poco mejor quewhile true ; do something ; sleep 1 ; done. Al menos se detiene fácilmente al presionar Ctrl + C.swarminglogic escribió un script llamado watchfile.sh , también disponible como GitHub Gist .
fuente
statlos nombres de archivo dados, se ejecutamd5sumen la salida y vuelve a ejecutar el comando dado si este valor cambia. Debido a que es Bash, sospecho que hace un buen trabajo al ejecutar el comando dado exactamente como si lo hubiera escrito en un indicador de Bash. (Por el contrario, la mayoría de las soluciones aquí escritas en otros idiomas no podrán ejecutar comandos que, por ejemplo, contienen alias de shell comoll)Mejorado con la respuesta de Gilles .
Esta versión se ejecuta
inotifywaituna vez y supervisa los eventos (.eg:) amodifypartir de entonces. Tal queinotifywaitno es necesario volver a ejecutarlo en cada evento encontrado.¡Es rápido y rápido! (incluso cuando se supervisa un directorio grande de forma recursiva)
fuente
Un poco más en el lado de la programación, pero quieres algo como inotify . Hay implementaciones en muchos idiomas, como jnotify y pyinotify .
Esta biblioteca le permite monitorear archivos individuales o directorios completos, y devuelve eventos cuando se descubre una acción. La información devuelta incluye el nombre del archivo, la acción (crear, modificar, renombrar, eliminar) y la ruta del archivo, entre otra información útil.
fuente
Para aquellos de ustedes que buscan una solución de FreeBSD, este es el puerto:
fuente
Me gusta la simplicidad,
while inotifywait ...; do ...; donesin embargo, tiene dos problemas:do ...;se puede perderPor lo tanto, hice un script de ayuda que usa inotifywait sin esas limitaciones: inotifyexec
Te sugiero que pongas este script en tu camino, como en
~/bin/. El uso se describe simplemente ejecutando el comando.Ejemplo:
inotifyexec "echo test" -r .fuente
Solución mejorada de Sebastian con
watchcomando:watch_cmd.sh:Ejemplo de llamada:
Funciona pero tenga cuidado: el
watchcomando tiene errores conocidos (vea man): reacciona ante cambios solo en VISIBLE en las partes terminales de la-g CMDsalida.fuente
Podrías probar el reflejo .
Reflex es una pequeña herramienta para mirar un directorio y volver a ejecutar un comando cuando cambian ciertos archivos. Es ideal para ejecutar automáticamente tareas de compilación / pelusa / prueba y para volver a cargar su aplicación cuando cambia el código.
fuente
Una respuesta en línea que estoy usando para realizar un seguimiento de un cambio de archivo:
No necesita inicializar BF si sabe que la primera fecha es la hora de inicio.
Esto es simple y portátil. Hay otra respuesta basada en la misma estrategia usando un script aquí. Echa un vistazo también.
Uso: estoy usando esto para depurar y vigilar
~/.kde/share/config/plasma-desktop-appletsrc; que por alguna razón desconocida sigue perdiendo miSwitchTabsOnHover=falsefuente
Yo uso este script para hacerlo. Estoy usando inotify en modo monitor
Guarde esto como runatwrite.sh
ejecutará myfile.sh en cada escritura.
fuente
Para aquellos que usan OS X, puede usar un LaunchAgent para ver una ruta / archivo en busca de cambios y hacer algo cuando eso suceda. FYI - LaunchControl es una buena aplicación para crear / modificar / eliminar demonios / agentes fácilmente.
( ejemplo tomado de aquí )
fuente
Tengo un GIST para esto y el uso es bastante simple
https://gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55
fuente
Para las personas que encuentran esto buscando en Google los cambios en un archivo en particular , la respuesta es mucho más simple (inspirada en la respuesta de Gilles ).
Si desea hacer algo después de que se haya escrito un archivo en particular, así es como:
Guarde esto como, por ejemplo,
copy_myfile.shy coloque el.sharchivo en la/etc/init.d/carpeta para que se ejecute al inicio.fuente
La herramienta 'fido' puede ser otra opción para esta necesidad. Ver https://www.joedog.org/fido-home/
fuente
Como algunos otros lo han hecho, también he escrito una herramienta de línea de comandos ligera para hacer esto. Está completamente documentado, probado y modular.
Watch-Do
Instalación
Puede instalarlo (si tiene Python3 y pip) usando:
Uso
Úselo de inmediato ejecutando:
Descripción de las características
-w '*.py'o-w '**/*.py')-dbandera nuevamente)-rpara activar esto)fuente