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-tools
paquete de su distribución ):o
El primer fragmento es más simple, pero tiene una desventaja significativa: perderá los cambios realizados mientras
inotifywait
no se está ejecutando (en particular mientras semyfile
está 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--format
opció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.py
con un archivo diferente, en lugar de escribir en el existentemyfile
,inotifywait
morirá. Muchos editores trabajan de esa manera.Para superar esta limitación, use
inotifywait
en 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.sh
script fácil de usar , disponible en: bitbucket.org/denilsonsa/small_scripts/srcwhile sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; done
es fantastico. Gracias.inotifywait -e delete_self
Parece que funciona bien para mí.while inotifywait -e close_write myfile.py; do ./myfile.py; done
siempre 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; done
entr ( 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
-d
bandera; es un poco más largo, pero puede hacerwhile sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; done
frente a los nuevos archivos.brew install entr
lo 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:
FILE
puede ser un directorio Mira recursivamente con-r
. Use%f
para pasar el nombre del archivo al comando.fuente
when-changed FILE 'clear; COMMAND'
.when-changed
ahora es multiplataforma! Echa un vistazo a la última versión 0.3.0 :)¿Qué tal este guión? Utiliza el
stat
comando 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
stat
sería mejor responder la hora modificada cuando el archivo cambia?vmstat -d
para 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
:silent
comando.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-keys
si le toca estar en un ambiente así :).vimrc
archivoSi tiene
npm
instalado,nodemon
es 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/models
WatchPaths
clave como se muestra en mi enlace.Para aquellos que no pueden instalar
inotify-tools
como yo, esto debería ser útil:watch -d -t -g ls -lR
Este comando saldrá cuando cambie la salida,
ls -lR
enumerará 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,3
Ejemplo 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 *.foo
es 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*.foo
expandirme 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 lacmd
variable y ejecute"$@"
. Su secuencia de comandos no es adecuada para monitorear un archivo grande, pero eso podría solucionarse reemplazandocp
portouch -r
(solo necesita la fecha, no el contenido). En cuanto a la portabilidad, la-nt
prueba 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 *.log
creo que el ejemplo podría resolverse .tail -f
noclear
la 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 $dst
comando, a menos que se modifique el evento fs, luego ejecute elpython $src
comando.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 ; done
es 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
stat
los nombres de archivo dados, se ejecutamd5sum
en 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
inotifywait
una vez y supervisa los eventos (.eg:) amodify
partir de entonces. Tal queinotifywait
no 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 ...; done
sin 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
watch
comando:watch_cmd.sh
:Ejemplo de llamada:
Funciona pero tenga cuidado: el
watch
comando tiene errores conocidos (vea man): reacciona ante cambios solo en VISIBLE en las partes terminales de la-g CMD
salida.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=false
fuente
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.sh
y coloque el.sh
archivo 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'
)-d
bandera nuevamente)-r
para activar esto)fuente