¿Cómo ejecutar un comando cada vez que cambia un archivo?


434

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.


Posible duplicado entre sitios de: stackoverflow.com/questions/2972765/… (aunque aquí está en el tema =))
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

He hecho referencia antes de un duplicado de sitios cruzados y fue denegado: S;)
Francisco Tapia

44
La solución de Jonathan Hartley se basa en otras soluciones aquí y soluciona los grandes problemas que tienen las respuestas más votadas: perder algunas modificaciones y ser ineficiente. Cambie la respuesta aceptada a la suya, que también se mantiene en github en github.com/tartley/rerun2 (o en alguna otra solución sin esos defectos)
nealmcb

Respuestas:


405

Simple, usando inotifywait (instale el inotify-toolspaquete de su distribución ):

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

o

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

El primer fragmento es más simple, pero tiene una desventaja significativa: perderá los cambios realizados mientras inotifywaitno se está ejecutando (en particular mientras se myfileestá 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:

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

De cualquier manera, hay una limitación: si algún programa reemplaza myfile.pycon un archivo diferente, en lugar de escribir en el existente myfile, inotifywaitmorirá. Muchos editores trabajan de esa manera.

Para superar esta limitación, use inotifywaiten el directorio:

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

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).


46
He encapsulado todo esto (con bastantes trucos bash) en un sleep_until_modified.shscript fácil de usar , disponible en: bitbucket.org/denilsonsa/small_scripts/src
Denilson Sá Maia

14
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; donees fantastico. Gracias.
Rhys Ulerich

55
inotifywait -e delete_selfParece que funciona bien para mí.
Kos

3
Es simple pero tiene dos problemas importantes: los eventos pueden perderse (todos los eventos en el bucle) y la inicialización de inotifywait se realiza cada vez, lo que hace que esta solución sea más lenta para carpetas recursivas grandes.
Wernight

66
Por alguna razón, 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; done
ideasman42

166

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:

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

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:

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

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.


3
No maneja nuevos archivos y sus modificaciones.
Wernight

2
@Wernight: a partir del 7 de mayo de 2014, entr tiene la nueva -dbandera; es un poco más largo, pero puede hacer while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; donefrente a los nuevos archivos.
Paul Fenney

1
entr también está disponible en repositorios debian al menos desde debian jessie / 8.2 en ...
Peter V. Mørch

55
el mejor que encontré en el OS X seguro. fswatch toma demasiados eventos funky y no quiero pasar el tiempo para descubrir por qué
dtc

55
Vale la pena señalar que entr está disponible en Homebrew, por brew install entrlo que funcionará como se esperaba
jmarceli

108

Escribí un programa de Python para hacer exactamente esto llamado cuándo se cambió .

El uso es simple:

when-changed FILE COMMAND...

O para ver múltiples archivos:

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

FILEpuede ser un directorio Mira recursivamente con -r. Use %fpara pasar el nombre del archivo al comando.


1
@ysangkok sí, en la última versión del código :)
joh

44
Ahora disponible desde "pip install when-changed". Aún funciona bien. Gracias.
AL Flanagan

2
Para borrar la pantalla primero puede usar when-changed FILE 'clear; COMMAND'.
Dave James Miller

1
Esta respuesta es mucho mejor porque también puedo hacerlo en Windows. Y este tipo realmente escribió un programa para obtener la respuesta.
Wolfpack'08

44
¡Buenas noticias para todos! when-changedahora es multiplataforma! Echa un vistazo a la última versión 0.3.0 :)
joh

52

¿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).

#!/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

2
¿No statsería mejor responder la hora modificada cuando el archivo cambia?
Xen2050

1
¿La ejecución de estadísticas muchas veces por segundo provocaría muchas lecturas en el disco? ¿o la llamada al sistema fstat automáticamente hace caché estas respuestas de alguna manera? Estoy tratando de escribir una especie de 'vigilancia gruñona' para compilar mi código c cada vez que hago cambios
Oskenso Kashi

Esto es bueno si conoce el nombre de archivo que se debe ver de antemano. Mejor sería pasar el nombre del archivo al script. Mejor aún sería si pudiera pasar muchos nombres de archivo (por ejemplo, "mywatch * .py"). Mejor aún sería si pudiera operar recursivamente en archivos en subdirectorios, lo que hacen algunas de las otras soluciones.
Jonathan Hartley

55
En caso de que alguien se pregunte sobre lecturas pesadas, probé este script en Ubuntu 17.04 con una suspensión de 0.05s y vmstat -dpara ver si hay acceso al disco. Parece que Linux hace un trabajo fantástico al almacenar en caché este tipo de cosas: D
Oskenso Kashi

Hay un error tipográfico en "COMANDO", estaba tratando de corregirlo, pero SO dice "Editar no debe tener menos de 6 caracteres"
user337085

30

Solución usando Vim:

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

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.


1
Esto puede ser muy agradable cuando se combina con 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 ... o tmux send-keyssi le toca estar en un ambiente así :)
Paul Fenney

¡bonito! puedes hacer una macro para tu .vimrcarchivo
ErichBSchulz

23

Si 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.


55
Sin embargo, solo mira archivos .js y .coffee.
zelk

66
La versión actual parece admitir cualquier comando, por ejemplo: nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models
kek

1
Desearía tener más información, pero osx tiene un método para rastrear cambios, eventos
ConstantineK

1
En OS X también puede usar Launch Daemons con una WatchPathsclave como se muestra en mi enlace.
Adam Johns

19

Para aquellos que no pueden instalar inotify-toolscomo yo, esto debería ser útil:

watch -d -t -g ls -lR

Este 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:

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

Sé que esta respuesta no puede ser leída por nadie, pero espero que alguien llegue a ella.

Ejemplo de línea de comando:

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

Abre otra terminal:

~ $ echo "testing" > /tmp/test

Ahora saldrá el primer terminal 1,2,3

Ejemplo de script simple:

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

watch -d -t -g ls -lR ${DIR_TO_WATCH} && ${COMMAND}

55
Buen truco. Lo probé y parece tener un problema cuando la lista es larga y el archivo modificado queda fuera de la pantalla. Una pequeña modificación podría ser algo como esto: watch -d -t -g "ls -lR tmp | sha1sum"
Atle

3
si observa su solución cada segundo, funciona para siempre y ejecuta MY_COMMAND solo si algunos archivos cambian: watch -n1 "watch -d -t -g ls -lR && MY_COMMAND"
mnesarco

Mi versión de watch (en Linux watch from procps-ng 3.3.10) acepta segundos flotantes para su intervalo, por watch -n0.2 ...lo tanto , sondeará cada quinto de segundo. Bueno para esas pruebas unitarias saludables de menos de milisegundos.
Jonathan Hartley

15

rerun2( en github ) es un script Bash de 10 líneas de la forma:

#!/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

Guarde la versión de github como 'volver a ejecutar' en su RUTA e invoque usando:

rerun COMMAND

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:

  • Utiliza inotify, por lo que responde mejor que las encuestas. Fabuloso para ejecutar pruebas unitarias por debajo de milisegundos, o renderizar archivos de puntos graphviz, cada vez que presiona 'guardar'.
  • Debido a que es tan rápido, no tiene que molestarse en decirle que ignore subdirecciones grandes (como node_modules) solo por razones de rendimiento.
  • Es súper receptivo adicional, porque solo llama a inotifywait una vez, al inicio, en lugar de ejecutarlo, e incurre en el costoso éxito de establecer relojes, en cada iteración.
  • Son solo 12 líneas de Bash
  • Debido a que es Bash, interpreta los comandos que le pasa exactamente como si los hubiera escrito en un indicador de Bash. (Presumiblemente, esto es menos genial si usa otro caparazón).
  • No pierde eventos que suceden mientras se ejecuta COMMAND, a diferencia de la mayoría de las otras soluciones de inotify en esta página.
  • En el primer evento, entra en un "período muerto" durante 0,15 segundos, durante el cual se ignoran otros eventos, antes de que se ejecute COMMAND exactamente una vez. Esto es para que la ráfaga de eventos causados ​​por el baile de creación-escritura-movimiento que Vi o Emacs hace al guardar un búfer no provoque múltiples ejecuciones laboriosas de un conjunto de pruebas posiblemente lento. Cualquier evento que ocurra mientras se ejecuta COMMAND no se ignora: causarán un segundo período muerto y la ejecución posterior.

Cosas que a uno no le gustarían:

  • Utiliza inotify, por lo que no funcionará fuera de Linuxland.
  • Debido a que usa inotify, se molestará al tratar de mirar directorios que contienen más archivos que el número máximo de relojes de inotify del usuario. Por defecto, esto parece estar configurado en alrededor de 5,000 a 8,000 en diferentes máquinas que uso, pero es fácil de aumentar. Ver https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • No puede ejecutar comandos que contienen alias Bash. Podría jurar que esto solía funcionar. En principio, como se trata de Bash, no se ejecuta COMMAND en una subshell, espero que esto funcione. Me encantaría saber si alguien sabe por qué no. Muchas de las otras soluciones en esta página tampoco pueden ejecutar dichos comandos.
  • Personalmente, desearía poder presionar una tecla en el terminal en el que se está ejecutando para provocar manualmente una ejecución adicional de COMMAND. ¿Podría agregar esto de alguna manera, simplemente? ¿Un ciclo simultáneo 'while read -n1' que también llama a execute?
  • En este momento lo he codificado para borrar el terminal e imprimir el COMANDO ejecutado en cada iteración. A algunas personas les gustaría agregar banderas de línea de comandos para desactivar cosas como esta, etc. Pero esto aumentaría el tamaño y la complejidad en muchas ocasiones.

Este es un refinamiento de la respuesta de @ cychoi.


2
Creo que debería usar en "$@"lugar de $@, para trabajar correctamente con argumentos que contienen espacios. Pero al mismo tiempo que usa eval, lo que obliga al usuario de la repetición a tener mucho cuidado al citar.
Denilson Sá Maia

Gracias Denilson ¿Podría dar un ejemplo de dónde las citas deben hacerse con cuidado? Lo he estado usando las últimas 24 horas y no he visto ningún problema con los espacios hasta ahora, ni he citado nada cuidadosamente , solo invoqué como rerun 'command'. ¿Está diciendo que si usé "$ @", el usuario podría invocar como rerun 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.
Jonathan Hartley

1
Algo así rerun foo "Some File"podría romperse. Pero como lo está utilizando eval, puede reescribirse como rerun '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 uso rerun 'touch *.foo'tiene una semántica ligeramente diferente (la expansión de la ruta ocurre solo una vez o varias veces).
Denilson Sá Maia

Gracias por la ayuda. Sí: se 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ómo rerun '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 ...
Jonathan Hartley

Más discusión sobre este PR ( github.com/tartley/rerun2/pull/1 ) y otros.
Jonathan Hartley

12

Aquí hay un simple script de shell Bourne que:

  1. Toma dos argumentos: el archivo a monitorear y un comando (con argumentos, si es necesario)
  2. Copia el archivo que está monitoreando en el directorio / tmp
  3. Comprueba cada dos segundos para ver si el archivo que está monitoreando es más reciente que la copia
  4. Si es más nuevo, sobrescribe la copia con el original más nuevo y ejecuta el comando
  5. Se limpia después de sí mismo cuando presiona Ctr-C

    #!/bin/sh  
    f=$1  
    shift  
    cmd=$*  
    tmpf="`mktemp /tmp/onchange.XXXXX`"  
    cp "$f" "$tmpf"  
    trap "rm $tmpf; exit 1" 2  
    while : ; do  
        if [ "$f" -nt "$tmpf" ]; then  
            cp "$f" "$tmpf"  
            $cmd  
        fi  
        sleep 2  
    done  
    

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.


99
El sondeo es la única forma portátil, pero la mayoría de los sistemas tienen un mecanismo de notificación de cambio de archivo (inotify en Linux, kqueue en FreeBSD, ...). Tiene un problema grave de cotización cuando lo hace $cmd, pero afortunadamente eso es fácilmente reparable: deshaga la cmdvariable y ejecute "$@". Su secuencia de comandos no es adecuada para monitorear un archivo grande, pero eso podría solucionarse reemplazando cppor touch -r(solo necesita la fecha, no el contenido). En cuanto a la portabilidad, la -ntprueba requiere bash, ksh o zsh.
Gilles

8

Echa un vistazo a incron . Es similar a cron, pero usa eventos inotify en lugar de tiempo.


Esto puede hacerse funcionar, pero crear una entrada incron es un proceso bastante laborioso en comparación con otras soluciones en esta página.
Jonathan Hartley

6

Otra solución con NodeJs, fsmonitor :

  1. Instalar en pc

    sudo npm install -g fsmonitor
    
  2. Desde la línea de comando (ejemplo, supervisar registros y "venta minorista" si un archivo de registro cambia)

    fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
    

Nota al margen: tail -F -q *.logcreo que el ejemplo podría resolverse .
Volker Siegel

Fue solo para dar un ejemplo, tail -fno clearla terminal.
Atika

6

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.


6

si tiene instalado nodemon , puede hacer esto:

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

En mi caso, edito html localmente y lo envío a mi servidor remoto cuando cambia un archivo.

nodemon -w <watch directory> -x "scp filename jaym@jay-remote.com:/var/www" -e ".html"

6

Bajo Linux:

man watch

watch -n 2 your_command_to_run

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.


Eso es bastante simple, aunque es un desperdicio, es fácil para tareas de desarrollo como hacer cambios en vivo a los estilos.
Xeoncross

2
¿Qué sucede cuando el comando tarda más de dos segundos en ejecutarse?
treinta

@thirtythreeforty Un experimento rápido en Ubuntu muestra que watch esperará los dos segundos completos sin importar cuánto tiempo tarde en ejecutarse el comando. FWIW, el período de suspensión se puede especificar con '-n', hasta un mínimo de 0.1 segundos.
Jonathan Hartley

5

Watchdog es un proyecto de Python, y puede ser justo lo que estás buscando:

Plataformas soportadas

  • Linux 2.6 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD / BSD (kqueue)
  • Windows (ReadDirectoryChangesW con puertos de finalización de E / S; subprocesos de trabajo ReadDirectoryChangesW)
  • Independiente del sistema operativo (sondear el disco en busca de instantáneas de directorio y compararlas periódicamente; lento y no recomendado)

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 el python $srccomando.

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

Usando argumentos cortos y restringiendo la ejecución solo cuando los eventos involucran " main .py":

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

EDITAR: Acabo de encontrar que Watchdog tiene una CLI oficial llamada watchmedo, así que échale un vistazo también.


4

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

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

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).


11
while sleep 1 ; do something ; donees un poco mejor que while true ; do something ; sleep 1 ; done. Al menos se detiene fácilmente al presionar Ctrl + C.
Denilson Sá Maia

¿Eliminar la suspensión causará un bucle ocupado (la CPU genera calor y daña la vida de la batería en una computadora portátil)?
Steven Lu

2
@StevenLu: no, el sueño no es una espera ocupada. El problema es que si el sueño está en el cuerpo, Control-C matará el sueño y el ciclo comenzará de nuevo. El uso de energía para comenzar el ciclo es insignificante. Pruébalo tú mismo en una terminal. Debe mantener Control-C para que funcione, si duerme en el cuerpo.
Janus Troelsen

Derecha. Creo que me lo perdí y no vi que el sueño todavía está presente como condición de bucle. Ese pequeño ajuste es bastante impresionante.
Steven Lu

4

2
Este es un script Bash de 200 líneas repleto de funciones que sondea statlos nombres de archivo dados, se ejecuta md5sumen 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 como ll)
Jonathan Hartley

4

Mejorado con la respuesta de Gilles .

Esta versión se ejecuta inotifywaituna vez y supervisa los eventos (.eg:) a modifypartir de entonces. Tal que inotifywait 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)

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

Esta es la mejor respuesta en la página para usuarios exclusivos de Linux. Reemplace las cosas dentro del bucle con 'execute $ @', y el usuario podría llamar a este script pasando su propio comando para ejecutar. Incluso funciona con comandos que contienen alias de shell si lo obtiene, usando algo como ". Scriptname COMMAND". Esto todavía encontrará el nombre del script en la RUTA.
Jonathan Hartley

Creo que quieres decir 'mientras lees RESPUESTA'?
Jonathan Hartley

1
gracias por la aclaración. Gracias por la fase de la misma! Hubiera eliminado esos comentarios, pero por supuesto ahora no lo haré.
Jonathan Hartley

3

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.


3

Para aquellos de ustedes que buscan una solución de FreeBSD, este es el puerto:

/usr/ports/sysutils/wait_on

3

Me gusta la simplicidad, while inotifywait ...; do ...; donesin embargo, tiene dos problemas:

  • Cambios en los archivos que suceden durante el do ...;se puede perder
  • Lento cuando se usa en modo recursivo

Por 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 .


Se actualizó el script para admitir la coincidencia de patrones regex.
Wernight

Ambos problemas se resuelven usando inotifywait en modo "--monitor". Ver la respuesta de cychoi.
Jonathan Hartley

3

Solución mejorada de Sebastian con watchcomando:

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

Ejemplo de llamada:

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

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.


2

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.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make

¿Puedes citar / explicar un poco sobre la herramienta? Lea rápidamente cómo recomendar software para orientación.
bertieb

1

Una respuesta en línea que estoy usando para realizar un seguimiento de un cambio de archivo:

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

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


1

Yo uso este script para hacerlo. Estoy usando inotify en modo monitor

#!/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

Guarde esto como runatwrite.sh

Usage: runatwrite.sh myfile.sh

ejecutará myfile.sh en cada escritura.


1

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í )

<?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

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:

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

Guarde esto como, por ejemplo, copy_myfile.shy coloque el .sharchivo en la /etc/init.d/carpeta para que se ejecute al inicio.


Comparte el problema con la respuesta de Giles de que ejecuta inotifywait en cada iteración, que puede no responder para ver recursivamente directorios muy grandes. Vea la respuesta de cychoi para la solución a esto.
Jonathan Hartley


0

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:

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

Uso

Úselo de inmediato ejecutando:

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

Descripción de las características

  • Admite el bloqueo de archivos (uso -w '*.py'o -w '**/*.py')
  • Ejecute múltiples comandos en un cambio de archivo (solo especifique la -dbandera nuevamente)
  • Mantiene dinámicamente la lista de archivos para ver si se usa globbing ( -rpara activar esto)
  • Múltiples formas de "mirar" un archivo:
    • Tiempo de modificación (predeterminado)
    • Archivo hash
    • Trivial para implementar el suyo (este es el Observador ModificationTime )
  • Diseño modular. Si desea que se ejecuten comandos, cuando se accede a un archivo, es trivial escribir su propio observador (mecanismo que determina si los ejecutores deben ejecutarse).
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.