2010-08-27 20:02:40 +0000 2010-08-27 20:02:40 +0000
459
459

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

Quiero una forma rápida y sencilla de ejecutar un comando cada vez que un archivo cambia. Quiero algo muy simple, algo que dejaré funcionando en una terminal y lo cerraré cuando termine de trabajar con ese archivo.

Actualmente, estoy usando esto:

while read; do ./myfile.py ; done

Y luego necesito ir a esa terminal y presionar Enter, siempre 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 esa.

BTW: Estoy usando Vim, y sé que puedo añadir un autocomando para ejecutar algo en BufWrite, pero este no es el tipo de solución que quiero ahora.

Actualizar: Quiero algo simple, descartable si es posible. Es más, quiero que algo se ejecute en una terminal porque quiero ver la salida del programa (quiero ver los mensajes de error).

Acerca de las respuestas: ¡Gracias por todas sus respuestas! Todas son muy buenas, y cada una tiene un enfoque muy diferente de las otras. Como sólo tengo que aceptar una, acepto la que he usado realmente (era simple, rápida y fácil de recordar), aunque sé que no es la más elegante.

Respuestas (37)

434
434
434
2010-08-27 20:54:55 +0000

Simple, usando inotifywait (instale el paquete inotify-tools 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: se perderá los cambios realizados mientras inotifywait no está funcionando (en particular mientras myfile está funcionando). El segundo fragmento no tiene este defecto. Sin embargo, tenga cuidado de que asuma que el nombre del archivo no contiene espacios en blanco. Si eso es un problema, use la opción --format para cambiar la salida para que no incluya 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.py por un archivo diferente, en lugar de escribir en el myfile existente, inotifywait morirá. Muchos editores trabajan de esa manera.

Para superar esta limitación, use inotifywait en 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).

179
179
179
2013-10-25 09:41:16 +0000

entr http://entrproject.org/ ) proporciona una interfaz más amigable para inotizar (y también soporta *BSD y Mac OS X).

Hace que sea muy fácil especificar múltiples archivos para ver (limitado sólo por ulimit -n), elimina la molestia de tener que lidiar con archivos que se están reemplazando, y requiere menos sintaxis de bash:

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

Lo he estado usando en todo el árbol de fuentes 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.

Banderas como -c (borrar la pantalla entre ejecuciones) y -d (salir cuando se añade un nuevo archivo a un directorio monitorizado) añaden aún más flexibilidad, por ejemplo puedes 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); la construcción a partir del repositorio del autor fue sin problemas en cualquier caso.

112
112
112
2011-06-30 13:34:28 +0000

Escribí un programa Python para hacer exactamente esto llamado cuando cambie .

El uso es simple:

when-changed FILE COMMAND...

O para ver múltiples archivos:

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

FILE puede ser un directorio. Mira recursivamente con -r. Usa %f para pasar el nombre del archivo al comando.

53
53
53
2013-08-20 17:12:47 +0000

¿Qué tal este guión? Utiliza el comando stat 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
30
30
30
2010-08-27 20:12:25 +0000

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 (necesidad de ejecutar :au! BufWritePost myfile.py). Además, esta solución bloquea a Vim hasta que el comando termine de ejecutarse.

He añadido esta solución aquí sólo para completar, ya que podría ayudar a otras personas.

Para mostrar la salida del programa (y para interrumpir por completo el flujo de edición, ya que la salida se escribirá sobre el editor durante unos segundos, hasta que pulses Intro), elimina el comando :silent.

23
23
23
2012-06-09 23:51:16 +0000

Si por casualidad tienes npm instalado, nodemon es probablemente la forma más fácil de empezar, especialmente en OS X, que aparentemente no tiene herramientas inotify. Soporta la ejecución de un comando cuando una carpeta cambia.

21
21
21
2016-03-22 14:57:03 +0000

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 la salida cambie, ls -lR listará cada archivo y directorio con su tamaño y fechas, así que si un archivo es cambiado debería salir del comando, como dice el hombre:

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

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

Ejemplo de línea de comandos:

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

Abrir otra terminal:

~ $ echo "testing" > /tmp/test

Ahora la primera terminal dará como resultado 1,2,3

Ejemplo de guión simple:

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

watch -d -t -g ls -lR ${DIR_TO_WATCH} && ${COMMAND}
17
17
17
2015-09-09 21:49:14 +0000

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

Guarda la versión de github como ‘rerun’ en tu PATH, e invócalo usando:

rerun COMMAND

Ejecuta COMMAND cada vez que hay un evento de modificación del sistema de archivos dentro de tu directorio actual (recursivo. )

Cosas que a uno le pueden gustar:

  • Utiliza inotify, por lo que es más sensible que el sondeo. Fabuloso para ejecutar pruebas de unidad de submilisegundos, o para renderizar archivos de puntos de graphviz, cada vez que le das a ‘guardar’.
  • Como es tan rápido, no tienes que molestarte en decirle que ignore las subdivisiones grandes (como node_modules) sólo por razones de rendimiento.
  • Es extra súper sensible, porque sólo llama inotifywait una vez, al inicio, en lugar de ejecutarlo, e incurre en el costoso golpe de establecer relojes, en cada iteración.
  • Son sólo 12 líneas de Bash
  • Porque es Bash, interpreta los comandos que le pasas exactamente como si los hubieras escrito en un prompt de Bash. (Presumiblemente esto es menos genial si usas otro shell.)
  • No pierde los eventos que ocurren mientras se ejecuta COMMAND, a diferencia de la mayoría de las otras soluciones inotificadas de esta página.
  • En el primer evento, entra en un ‘período muerto’ de 0,15 segundos, durante el cual se ignoran otros eventos, antes de que COMMAND se ejecute exactamente una vez. Esto es para que la oleada de eventos causados por la danza de crear-escribir-mover que Vi o Emacs hacen al salvar un buffer no causen múltiples ejecuciones laboriosas de un conjunto de pruebas posiblemente lento. Cualquier evento que ocurra mientras se ejecuta COMMAND no es ignorado - causará un segundo período muerto y la subsiguiente ejecución.

Cosas que a uno le pueden disgustar:

  • Utiliza inotify, por lo que no funcionará fuera de Linuxland.
  • Debido a que utiliza inotify, vomitará al tratar de ver directorios que contengan más archivos que el número máximo de relojes inotify de usuario. Por defecto, esto parece estar configurado 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
  • Falla en la ejecución de comandos que contienen alias de Bash. Podría jurar que esto solía funcionar. En principio, porque esto es Bash, no ejecutando COMMAND en una subcarpeta, esperaría que esto funcionara. Me encantaría escuchar si alguien sabe por qué no lo hace. Muchas de las otras soluciones en esta página tampoco pueden ejecutar tales comandos.
  • Personalmente me gustaría poder pulsar una tecla en el terminal en el que se está ejecutando para causar manualmente una ejecución extra de COMANDO. ¿Podría agregar esto de alguna manera, simplemente? ¿Un bucle simultáneo ‘while read -n1’ que también llame a ejecutar?
  • Ahora mismo lo he codificado para borrar la terminal e imprimir el COMANDO ejecutado en cada iteración. A algunas personas les gustaría añadir banderas de línea de comandos para apagar cosas como esta, etc. Pero esto aumentaría mucho el tamaño y la complejidad.

Este es un refinamiento del anwer de @cychoi.

12
12
12
2010-08-27 21:23:29 +0000

Aquí hay un simple script de shell Bourne que:

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

Esto funciona en FreeBSD. El único problema de portabilidad que se me ocurre es si algún otro Unix no tiene el comando mktemp(1), pero en ese caso sólo se puede codificar el nombre del archivo temporal.

8
8
8
2014-08-08 22:20:09 +0000

si tienes nodemon instalado, entonces puedes hacer esto:

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

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

nodemon -w <watch directory> -x "scp filename jaym@jay-remote.com:/var/www" -e ".html"
8
8
8
2010-08-27 20:12:59 +0000

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

6
6
6
2014-07-09 13:16:10 +0000

Mira en Guard, en particular con este plugin: https://github.com/hawx/guard-shell

Puedes configurarlo para ver cualquier número de patrones en el directorio de tu proyecto, y ejecutar comandos cuando ocurran cambios. Es muy probable que incluso haya un plugin disponible para eso que estás tratando de hacer en primer lugar.

6
6
6
2014-01-22 14:55:46 +0000

Otra solución con NodeJs, * fsmonitor ** :

  1. Instalar

  2. Desde la línea de comandos (ejemplo, monitorizar registros y “retail” si un archivo de registro cambia)

5
5
5
2015-12-28 10:44:18 +0000
  • Watchdog ** es un proyecto 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; hilos de trabajo ReadDirectoryChangesW)
  • Independiente del sistema operativo (sondeando el disco en busca de instantáneas de directorios y comparándolas periódicamente; lento y no recomendado)

Acaba de escribir un envoltorio de línea de comandos para ello * watchdog_exec **:

Ejemplo ejecuta

En el evento fs que involucra archivos y carpetas en el directorio actual, ejecuta el comando echo $src $dst, a menos que el evento fs sea modificado, entonces ejecuta el comando python $src.

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

Usando argumentos cortos, y restringiendo para ejecutar solamente cuando los eventos involucran “ main.py”:

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

EDITAR: Acabo de encontrar que Watchdog tiene un CLI oficial llamado watchmedo, así que compruébalo también.

5
5
5
2012-07-02 16:36:51 +0000

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é hecho antes de hacerlo de nuevo.

4
4
4
2010-08-27 20:37:52 +0000

Si su programa genera algún tipo de registro/salida, puede crear un Makefile con una regla para ese registro/salida que dependa de su script y hacer algo como

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

Alternativamente, puede crear un objetivo falso y hacer que la regla para ello llame a su script y toque el objetivo falso (mientras siga dependiendo de su script).

4
4
4
2014-09-15 23:24:58 +0000

swarminglogic escribió un guión llamado watchfile.sh, también disponible como GitHub Gist.

4
4
4
2015-06-17 18:03:51 +0000

Mejorado sobre la respuesta de Gilles .

Esta versión ejecuta inotifywait una vez y monitoriza los eventos (.e.g.: modify) a partir de entonces. De tal manera que inotifywait _no necesita ser re-ejecutado en cada evento encontrado.

Es rápido y veloz!(incluso cuando se monitorea un gran directorio 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
3
3
3
2010-08-27 20:05:59 +0000

Un poco más en el lado de la programación, pero quieres algo como inotify . Hay implementaciones en muchos lenguajes, como jnotify y pyinotify .

Esta librería te permite monitorear archivos individuales o directorios enteros, y devuelve eventos cuando se descubre una acción. La información devuelta incluye el nombre del archivo, la acción (crear, modificar, renombrar, borrar) y la ruta del archivo, entre otra información útil.

3
3
3
2012-11-11 21:48:31 +0000

Para aquellos de ustedes que están buscando una solución para FreeBSD, aquí está el port:

/usr/ports/sysutils/wait_on
3
3
3
2014-04-29 13:56:13 +0000

Me gusta la simplicidad de while inotifywait ...; do ...; done sin embargo tiene dos problemas:

  • Los cambios en los archivos que ocurren durante el do ...; se perderán
  • Lento cuando se usa en modo recursivo

Por eso hice un script de ayuda que usa inotifywait sin esas limitaciones: Sugiero que pongas este script en tu camino, como en ~/bin/. El uso se describe con sólo ejecutar el comando.

Ejemplo: inotifyexec "echo test" -r .

3
3
3
2016-04-12 14:53:44 +0000

Mejorado La solución de Sebastian con el comando watch:

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 ten cuidado: el comando watch tiene errores conocidos (ver el hombre): reacciona a los cambios sólo en VISIBLE en las partes terminales de la salida de -g CMD.

2
2
2
2017-03-01 20:14:14 +0000

Podrías probar reflex .

Reflex es una pequeña herramienta para ver un directorio y volver a ejecutar un comando cuando ciertos archivos cambian. Es genial para ejecutar automáticamente tareas de compilación/huella/prueba y para recargar tu aplicación cuando el código cambia.

# Rerun make whenever a .c file changes
reflex -r '\.c$' make
1
1
1
2017-04-05 07:38:43 +0000

Tengo un GIST para esto y el uso es bastante simple

watchfiles <cmd> <paths...>

https://gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55

1
1
1
2017-06-03 12:10:57 +0000

Como algunos otros lo han hecho, también he escrito una herramienta de línea de comandos ligera para hacer esto. Está completamente documentada, probada y modular.

Watch-Do

Instalación

Puedes instalarla (si tienes Python3 y pip) usando:

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

Uso

Usarlo inmediatamente ejecutándolo:

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

Características Descripción general

  • Soporta globbing de archivos (use -w '*.py' o -w '**/*.py')
  • Ejecuta múltiples comandos en un cambio de archivo (sólo especifique de nuevo el indicador -d)
  • Mantiene dinámicamente la lista de archivos a observar si se usa globbing (-r para activarlo)
  • Múltiples formas de “observar” un archivo:
  • Tiempo de modificación (por defecto)
  • Hash de archivo
  • Trivial para implementar el suyo propio (este es el ModificationTime watcher)
  • Diseño modular. Si quieres que se ejecuten los comandos, cuando se accede a un archivo, es trivial escribir tu propio watcher (mecanismo que determina si los hacedores deben ser ejecutados).
1
1
1
2016-02-26 19:10:05 +0000

Uso este guión 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

Guarda esto como runatwrite.sh

Usage: runatwrite.sh myfile.sh

ejecutará myfile.sh en cada escritura.

1
1
1
2015-09-09 19:36:04 +0000

Escribí un programa Python para hacer exactamente esto, llamado rerun .

ACTUALIZACIÓN: Esta respuesta es un script Python que sondea los cambios, lo cual es útil en algunas circunstancias. Para un script Bash sólo para Linux que usa inotify, vea mi otra respuesta, busque en esta página ‘rerun2’.

Instalar para Python2 o Python3 con:

pip install --user rerun

y el uso es muy simple:

rerun "COMMAND"

El comando se espera como un solo arg, no una secuencia de args separados por el espacio. Por lo tanto, cítelo como se muestra, lo que reduce cualquier escape extra que tenga que añadir. Simplemente escribe el comando como lo habrías escrito en la línea de comandos, pero rodeado de comillas.

De forma predeterminada observa todos los archivos en o bajo el directorio actual, omitiendo cosas como los dirs de control de fuente conocidos, .git, .svn, etc.

Las banderas opcionales incluyen ‘-i NAME’ que ignora los cambios en los archivos o directorios nombrados. Esto se puede dar varias veces.

Como es un script en Python, necesita ejecutar el comando como un subproceso, y usamos una nueva instancia del shell actual del usuario para interpretar ‘COMMAND’ y decidir qué proceso ejecutar realmente. Sin embargo, si su comando contiene alias de shell y similares que están definidos en .bashrc, estos no serán cargados por el sub-proceso. Para arreglar esto, puedes darle a la reejecución una bandera ‘-I’, para usar sub-shells interactivos (alias ‘login’). Esto es más lento y más propenso a errores que iniciar un shell regular, porque tiene que originar tu .bashrc.

Yo lo uso con Python 3, pero la última vez que revisé la reejecución aún funcionaba con Python 2.

Espada de doble filo es que usa polling en vez de inotify. Por el lado positivo, esto significa que funciona en todos los sistemas operativos. Además, es mejor que otras soluciones mostradas aquí en términos de ejecutar el comando una sola vez para un montón de cambios en el sistema de archivos, no una vez por cada archivo modificado, mientras que al mismo tiempo ejecuta el comando una segunda vez si algún archivo cambia de nuevo mientras se está ejecutando el comando.

Por el lado negativo, el sondeo significa que hay una latencia de 0,0 a 1,0 segundos, y por supuesto es lento para monitorear directorios extremadamente grandes. Dicho esto, nunca he encontrado un proyecto lo suficientemente grande como para que esto se note, siempre y cuando uses ‘-i’ para ignorar cosas grandes como tu virtualenv y tus módulos de nodos.

Hmmm. rerun ha sido indispensable para mí durante años - básicamente lo uso ocho horas cada día para hacer pruebas, reconstruir archivos de puntos a medida que los edito, etc. Pero ahora vengo a escribir esto aquí, está claro que necesito cambiar a una solución que use inotify (ya no uso Windows u OSX.) y que esté escrita en Bash (por lo que funciona con alias sin ningún tipo de manipulación extra.)

1
1
1
2015-09-01 04:53:52 +0000

Una respuesta de un solo uso que estoy usando para llevar la cuenta de un cambio de archivo:

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

No necesitas inicializar BF si sabes 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 guión aquí. Echa un vistazo también.

  • *

Uso: Estoy usando esto para depurar y mantener un ojo en ~/.kde/share/config/plasma-desktop-appletsrc; que por alguna razón desconocida sigue perdiendo mi SwitchTabsOnHover=false

1
1
1
2016-03-22 15:33:56 +0000

Para aquellos que usan OS X, pueden usar un LaunchAgent para ver una ruta/archivo de cambios y hacer algo cuando eso ocurra. Para tu información, LaunchControl es una buena aplicación para hacer/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
0
0
2019-11-26 15:53:22 +0000

Si no quieres instalar nada nuevo para esto, aquí tienes un pequeño shell-script que puedes poner en tu camino (por ejemplo, bajo $HOME/bin). Ejecuta un comando cuando se cambian uno o más archivos proporcionados. Por ejemplo:

$ onchange './build' *.txt
#!/bin/sh
cmd="$1"; shift
files="$@"
changed() { tar -c $files | md5sum; } # for on every save use: `stat -c %Z $files`
while true; do
  if ["$(changed)" != "$last"]; then
    last="$(changed)"
    $cmd
  fi
  sleep 1
done

Alquitrana, y luego hace un hash del contenido de los archivos y/o directorios, de modo que no se ejecutará cada vez que pulse compulsivamente CTRL-S (o escriba :w), sino sólo una vez que algo cambie realmente. Ten en cuenta que lo comprobará cada segundo, así que no lo incluyas demasiado o tu máquina podría volverse lenta. Si quieres que se ejecute en cada guardada, usa stat en lugar de (ver comentario). Además, para mac md5sum se llama md5 si lo recuerdo correctamente.

Pequeño truco ingenioso: En el momento en que quieras usarlo, probablemente querrás repetir el último comando que acabas de ejecutar, pero una y otra vez. Puedes usar el atajo !! para “inyectar” el último comando en este:

$ onchange "!!" *.txt
0
0
0
2018-04-12 18:32:28 +0000

Descripción

Esto observará un archivo para los cambios y ejecutará cualquier comando (incluyendo más argumentos) fue dado como segunda declaración. También limpiará la pantalla e imprimirá la hora de la última ejecución. Nota: puede hacer que la función sea más (o menos) reactiva cambiando el número de segundos que la función debe dormir después de cada ciclo de bucle while.

Ejemplo de uso

watch_file my_file.php php my_file.php

Esta línea observará un archivo php my_file.php y se ejecutará a través del intérprete php siempre que cambie.

Definición de la función

function watch_file (){

### Set initial time of file
LTIME=`stat -c %Z $1`
printf "&00133c"
echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
${@:2}

while true
do
   ATIME=`stat -c %Z $1`

   if [["$ATIME" != "$LTIME"]]
   then
    printf "&00133c"
    echo -e "watching: $1 ---- $(date '+%Y-%m-%d %H:%M:%S')\n-------------------------------------------\n"
    ${@:2}
    LTIME=$ATIME
   fi
   sleep 1
done
}

Crédito

Esta es básicamente una versión más general de la respuesta del VDR.

0
0
0
2018-03-19 20:30:21 +0000

Uso básico

Esta es una solución que no requiere la instalación de más software y que funciona de inmediato.

tail -q --follow=name myfile.txt | head -n 0

Este comando sale bajo las siguientes condiciones:

  • Se agrega una línea a myfile.txt después de que el comando se ejecuta
  • El myfile.txt se reemplaza con otro después de que el comando se ejecuta

Usted dice que está usando vim, y vim reemplazará el archivo al guardarlo. He probado que esto funciona con vim.

Puedes ignorar la salida de este comando, puede mencionar algo como:

cola: ‘miarchivo.txt’ ha sido reemplazado; siguiendo el final del nuevo archivo

Uso avanzado

Puedes combinar esto con timeout para devolver verdadero o falso. Puedes usarlo así:

timeout 5s bash -c ‘tail -q –follow=name pipe 2> /dev/null | head -n 0’ && echo changed || echo timeout

Discusión

tail usa inotify bajo el capó. Así es como se consigue este elegante comportamiento asíncrono sin ningún tipo de sondeo. Probablemente hay algún otro programa unix estándar que usa inotify del que podemos abusar más elegantemente.

A veces estos comandos salen enseguida pero si los ejecutas inmediatamente una segunda vez entonces funcionan como se anuncia. He cometido un error aislado en algún lugar, por favor ayúdame a corregirlo.

En RHEL puedo usar:

timeout 5s sh -c ‘gio monitor pipe | head -n 0’ && echo changed || echo timeout

Pero no estoy seguro de que sea portátil.

0
0
0
2019-01-28 16:51:42 +0000

find puede hacer el truco.

while true; do
    find /path/to/watched/file -ctime 1s | xargs do-what-you-will
done

find -ctime 1s imprime el nombre de archivo si fue cambiado en el último 1s.

0
0
0
2015-05-13 15:21:33 +0000

Para la gente que encuentra esto en Google para los cambios en un archivo particular, la respuesta es mucho más simple (inspirada en la respuesta de Gilles ).

Si quieres hacer algo después de que un archivo en particular haya sido escrito, aquí está cómo:

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

Guarda esto como, por ejemplo, copy_myfile.sh y pon el archivo .sh en la carpeta /etc/init.d/ para que se ejecute al inicio.

0
0
0
2019-03-21 11:33:37 +0000

Yo tenía una situación ligeramente diferente. Pero creo que esto puede ser útil para alguien que lea esta pregunta.

Necesitaba ser notificado cuando un archivo de registro cambiara de tamaño, pero no es necesario inmediatamente. Y podrían ser días o semanas en el futuro, por lo que no podía usar inotify (que de todas formas no estaba instalado/activado en ese servidor) en la línea de comandos (no quería usar nohup o similar). Así que decidí ejecutar un script bash en cron para comprobar

El script escribe el tamaño del archivo observado en un archivo de texto y en cada ejecución de cron comprueba, si ese valor ha cambiado y me envía por correo la última línea si ha cambiado

#!/bin/bash
FILE_TO_WATCH="/path/to/log_file.log"
FILESIZE_FILE="/path_to/record.txt"
SUBJECT="Log file 'log_file.log' has changed"
MAILTO="info@example.com"
BODY="Last line of log file:\n"
LAST_LINES=1

# get old recorded file size from file
OLD_FILESIZE=$(cat "${FILESIZE_FILE}")
# write current file size into file
stat --printf="%s" "${FILE_TO_WATCH}" > "${FILESIZE_FILE}"
# get new recorded file size from file
NEW_FILESIZE=$(cat "${FILESIZE_FILE}")

if ["${OLD_FILESIZE}" != "${NEW_FILESIZE}"]; then
    echo -e "${BODY}"$(tail -${LAST_LINES} ${FILE_TO_WATCH}) | mail -s "${SUBJECT}" "${MAILTO}"
fi
0
0
0
2019-11-23 23:41:53 +0000

Comprueba https://github.com/watchexec/watchexec .

watchexec es una herramienta simple y autónoma que observa un camino y ejecuta un comando cuando detecta modificaciones.

Ejemplo

Observa todos los archivos JavaScript, CSS y HTML en el directorio actual y todos los subdirectorios por cambios, ejecutando make cuando se detecta un cambio:

$ watchexec --exts js,css,html make

0
0
0
2017-03-20 13:52:53 +0000

La herramienta “fido” puede ser otra opción para esta necesidad. Ver https://www.joedog.org/fido-home/