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

Come eseguire un comando ogni volta che un file cambia?

Voglio un modo semplice e veloce per eseguire un comando ogni volta che un file cambia. Voglio qualcosa di molto semplice, qualcosa che lascerò in esecuzione su un terminale e lo chiuderò ogni volta che avrò finito di lavorare con quel file.

Attualmente, sto usando questo:

while read; do ./myfile.py ; done
``` ```
while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done

E poi devo andare su quel terminale e premere Invio, ogni volta che salvo quel file sul mio editor. Quello che voglio è qualcosa come questo:

&001 &001

o qualsiasi altra soluzione semplice come questa.

BTW: Sto usando Vim, e so che posso aggiungere un autocomando per eseguire qualcosa su BufWrite, ma questo non è il tipo di soluzione che voglio ora.

Aggiornamento: Voglio qualcosa di semplice, eliminabile se possibile. Inoltre, voglio qualcosa da eseguire in un terminale perché voglio vedere l'uscita del programma (voglio vedere i messaggi di errore).

Riguardo alle risposte: Grazie per tutte le risposte! Sono tutte molto buone, e ognuna di esse ha un approccio molto diverso dalle altre. Siccome devo accettarne solo una, accetto quella che ho effettivamente usato (era semplice, veloce e facile da ricordare), anche se so che non è la più elegante.

Risposte (37)

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

Semplice, usando inotifywait (installa il pacchetto inotify-tools della tua distribuzione):

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

Il primo frammento è più semplice, ma ha un significativo svantaggio: mancherà le modifiche eseguite mentre inotifywait non è in funzione (in particolare mentre myfile è in funzione). Il secondo snippet non ha questo difetto. Tuttavia, attenzione che presuppone che il nome del file non contenga spazi bianchi. Se questo è un problema, usare l'opzione --format per cambiare l'output per non includere il nome del file:

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

In ogni caso, c'è una limitazione: se qualche programma sostituisce myfile.py con un file diverso, invece di scrivere sul myfile esistente, inotifywait morirà. Molti editor lavorano in questo modo.

Per superare questa limitazione, usare inotifywait sulla directory:

&001

In alternativa, usare un altro strumento che usa la stessa funzionalità di base, come incron (permette di registrare gli eventi quando un file viene modificato) o fswatch (uno strumento che funziona anche su molte altre varianti Unix, usando l'analogo di ogni variante di inotify di Linux).

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

entr http://entrproject.org/ ) fornisce un'interfaccia più amichevole per inotizzare (e supporta anche *BSD & Mac OS X).

Rende molto facile specificare più file da guardare (limitato solo da ulimit -n), elimina la seccatura di trattare con i file che vengono sostituiti, e richiede meno sintassi bash:

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

L'ho usato su tutto il mio albero dei sorgenti del progetto per eseguire i test unitari per il codice che sto modificando, ed è già stato un enorme impulso al mio flusso di lavoro.

Flags come -c (cancella la schermata tra una esecuzione e l'altra) e -d (esci quando un nuovo file viene aggiunto ad una directory monitorata) aggiungono ancora più flessibilità, per esempio si può fare:

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


All'inizio del 2018 è ancora in sviluppo attivo e si trova in Debian & Ubuntu (`apt install entr`); la costruzione dal repo dell'autore era comunque indolore.
112
112
112
2011-06-30 13:34:28 +0000

Ho scritto un programma Python per fare esattamente questo chiamato quando-modificato .

L'uso è semplice:

when-changed FILE COMMAND...

o per guardare più file:

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

FILE può essere una directory. Guarda ricorsivamente con -r. Utilizzare %f per passare il nome del file al comando.

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

Che ne dite di questo copione? Utilizza il comando stat per ottenere il tempo di accesso di un file ed esegue un comando ogni volta che c'è un cambiamento nel tempo di accesso (ogni volta che si accede al file).

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

Soluzione che utilizza Vim:

:au BufWritePost myfile.py :silent !./myfile.py
``` &001 


Ma non voglio questa soluzione perché è un po' fastidioso da digitare, è un po' difficile ricordare cosa digitare, esattamente, ed è un po' difficile annullarne gli effetti (è necessario eseguire `:au! BufWritePost myfile.py`). Inoltre, questa soluzione blocca Vim fino a quando il comando non ha finito di essere eseguito. 

Ho aggiunto questa soluzione qui solo per completezza, in quanto potrebbe aiutare altre persone. 

Per visualizzare l'output del programma (e disturbare completamente il vostro flusso di editing, in quanto l'output scriverà sul vostro editor per alcuni secondi, fino a quando non si preme Invio), rimuovere il comando `:silent`.
23
23
23
2012-06-09 23:51:16 +0000

Se vi capita di avere npm installato, nodemon è probabilmente il modo più semplice per iniziare, specialmente su OS X, che apparentemente non ha strumenti inotify. Esso supporta l'esecuzione di un comando quando una cartella cambia.

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

Per coloro che non possono installare inotify-tools come me, questo dovrebbe essere utile:

watch -d -t -g ls -lR ls -lR

Questo comando uscirà quando l'output cambia, 1,2,3 elencherà ogni file e directory con la sua dimensione e le date, quindi se un file viene cambiato dovrebbe uscire dal comando, come dice l'uomo:

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

So che questa risposta potrebbe non essere letta da nessuno, ma spero che qualcuno la raggiunga.

Esempio di linea di comando:

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

Aprire un altro terminale:

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

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

Ora il primo terminale emetterà &007 


Esempio di script semplice: 


&001
17
17
17
2015-09-09 21:49:14 +0000

rerun2 su github ) è uno script Bash a 10 righe della 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
``` ```
rerun COMMAND

Salva la versione di github come ‘rerun’ sul tuo PATH, e richiamala usando:

&001

Esegue COMMAND ogni volta che c'è un evento di modifica del filesystem all'interno della tua directory corrente (ricorsivo. )

Cose che potrebbero piacere:

  • Utilizza inotify, quindi è più reattivo del polling. Favoloso per eseguire test unitari di sub-millisecondi, o per il rendering di file di punti graphviz, ogni volta che si preme ‘salva’.
  • Poiché è così veloce, non devi preoccuparti di dirgli di ignorare i grandi subdir (come i node_modules) solo per ragioni di prestazioni.
  • È super reattivo, perché chiama inotifywait una sola volta, all'avvio, invece di eseguirlo, e incorrere nel costoso colpo di stabilire orologi, su ogni iterazione.
  • Sono solo 12 righe di Bash
  • Perché è Bash, interpreta i comandi che si passano esattamente come se li aveste digitati ad un prompt di Bash. (Presumibilmente questo è meno figo se si usa un'altra shell.)
  • Non perde gli eventi che accadono mentre COMMAND è in esecuzione, a differenza della maggior parte delle altre soluzioni inotify di questa pagina.
  • Sul primo evento, entra in un ‘periodo morto’ per 0,15 secondi, durante il quale gli altri eventi vengono ignorati, prima che COMMAND venga eseguito esattamente una volta. Questo per evitare che il turbinio di eventi causati dalla danza creato-scrittura-movimento che Vi o Emacs fanno quando si salva un buffer non provochino molteplici esecuzioni laboriose di una suite di test possibilmente lenta. Qualsiasi evento che poi si verifichi durante l'esecuzione di COMMAND non viene ignorato - causerà un secondo periodo morto e la successiva esecuzione.

Cose che potrebbero non piacere:

  • Utilizza l'inotify, quindi non funzionerà al di fuori di Linuxland.
  • Poiché utilizza l'inotify, vomiterà nel cercare di guardare le directory che contengono più file del numero massimo di utenti che inotizzano gli orologi. Per impostazione predefinita, questo sembra essere impostato a circa 5.000-8.000 su diverse macchine che uso, ma è facile da aumentare. Vedi https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Non riesce ad eseguire comandi contenenti alias Bash. Potrei giurare che questo funzionava. In linea di principio, poiché questo è Bash, non eseguendo COMMAND in un sottogruppo, mi aspetterei che funzionasse. Mi piacerebbe sentire se qualcuno sa perché non funziona. Neanche molte delle altre soluzioni di questa pagina possono eseguire tali comandi.
  • Personalmente vorrei poter premere un tasto nel terminale in cui è in esecuzione per causare manualmente un'esecuzione extra di COMMAND. Potrei aggiungere questo in qualche modo, semplicemente? Un loop ‘while read -n1’ in esecuzione simultanea che chiama anche l'esecuzione di comandi?
  • In questo momento l'ho codificato per cancellare il terminale e stampare il COMANDO eseguito su ogni iterazione. Ad alcuni potrebbe piacere aggiungere delle bandiere a riga di comando per spegnere cose come questa, ecc. Ma questo aumenterebbe di molto le dimensioni e la complessità.

Questa è una raffinatezza dell'anwer di @cychoi.

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

Ecco un semplice script di conchiglia Bourne che:

  1. Prende due argomenti: il file da monitorare e un comando (con argomenti, se necessario)
  2. 2. Copia il file che si sta monitorando nella directory /tmp
  3. 3. Controlla ogni due secondi per vedere se il file che stai monitorando è più nuovo della copia
  4. Se è più nuovo sovrascrive la copia con l'originale più recente ed esegue il comando
  5. Pulisce dopo se stesso quando premi Ctr-C

Questo funziona su FreeBSD. L'unico problema di portabilità che mi viene in mente è se qualche altro Unix non ha il comando mktemp(1), ma in quel caso si può solo codificare il nome del file temp.

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

se avete nodemon installato, allora potete fare questo:

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

Nel mio caso modifico l'html localmente e lo spedisco al mio server remoto quando un file 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

Date un'occhiata a incron . È simile a cron, ma usa inotificare gli eventi invece del tempo.

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

Guardate in Guard, in particolare con questo plugin: https://github.com/hawx/guard-shell &003

È possibile impostarlo per guardare un numero qualsiasi di schemi nella directory del vostro progetto, ed eseguire comandi quando si verificano dei cambiamenti. Ci sono buone probabilità anche che ci sia un plugin disponibile per quello che si sta cercando di fare in primo luogo.

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

Un'altra soluzione con NodeJs, * fsmonitor ** :

  1. Install

  2. Da riga di comando (esempio, monitor logs e “retail” se un file di log cambia)

5
5
5
2015-12-28 10:44:18 +0000
  • Watchdog ** è un progetto Python, e potrebbe essere proprio quello che stai cercando:

Piattaforme supportate

  • Linux 2. 6 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD/BSD (kqueue)
  • Windows (ReadDirectoryChangesW con porte di completamento I/O; ReadDirectoryChangesW worker threads)
  • OS-independent (polling del disco per le istantanee delle directory e confronto periodico; lento e non consigliato)

Appena scritto un wrapper a riga di comando * watchdog_exec **:

L'esempio esegue

sull'evento fs che coinvolge file e cartelle nella directory corrente, eseguire il comando echo $src $dst, a meno che l'evento fs non venga modificato, quindi eseguire il comando python $src.

python -m watchdog_exec . --execute echo --modified python
``` ```
python -m watchdog_exec . -e echo -a echo -s __main__.py

Usando argomenti brevi, e limitando l'esecuzione solo quando gli eventi coinvolgono “ main.py”:

&001 &001


EDIT: Appena trovato Watchdog ha un CLI ufficiale chiamato watchmedo, quindi controllate anche questo.

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

Sotto Linux:

man watch

watch -n 2 your_command_to_run
``` &001 


eseguirà il comando ogni 2 secondi. 


Se il comando richiede più di 2 secondi per essere eseguito, l'orologio aspetterà fino a quando non sarà terminato prima di eseguirlo di nuovo.
4
4
4
2010-08-27 20:37:52 +0000

Se il vostro programma genera una sorta di log/output, potete creare un Makefile con una regola per quel log/output che dipende dal vostro script e fare qualcosa come

while true; do make -s my_target; sleep 1; done
``` &001 


In alternativa, potete creare un target fasullo e avere la regola per esso sia chiamare il vostro script che toccare il target fasullo (mentre ancora dipende dal vostro script).
4
4
4
2014-09-15 23:24:58 +0000

swarminglogic ha scritto uno script chiamato watchfile.sh , disponibile anche disponibile come GitHub Gist .

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

Migliorata sulla risposta di Gilles .

Questa versione gira inotifywait una volta e monitora gli eventi (.e.g.: modify) successivamente. Tale che inotifywait _non ha bisogno di essere eseguito nuovamente su ogni evento incontrato.

È veloce e veloce!(anche quando si monitorano grandi directory ricorsivamente)

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 po’ di più sul lato della programmazione, ma si vuole qualcosa come inotify . Ci sono implementazioni in molti linguaggi, come jnotify e pyinotify .

Questa libreria permette di monitorare singoli file o intere directory, e restituisce gli eventi quando un'azione viene scoperta. Le informazioni restituite includono il nome del file, l'azione (creare, modificare, rinominare, cancellare) e il percorso del file, tra le altre informazioni utili.

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

Per chi cerca una soluzione FreeBSD, ecco la porta:

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

Mi piace la semplicità di while inotifywait ...; do ...; done ma ha due problemi:

  • I cambiamenti di file che avvengono durante il do ...; saranno _perso
  • Lenta quando si usa in modalità ricorsiva

Per questo ho fatto uno script helper che usa inotifywait senza queste limitazioni: inotifyexec

Vi suggerisco di mettere questo script sul vostro percorso, come in ~/bin/. L'uso è descritto semplicemente eseguendo il comando.

Esempio: inotifyexec "echo test" -r .

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

Migliorata la soluzione di Sebastian con il 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
``` ```
watch_cmd.sh "ls -lR /etc/nginx | grep .conf$" "sudo service nginx reload"

Esempio di chiamata:

&001 &001

Funziona ma attenzione: il comando watch ha bug noti (vedi uomo): reagisce ai cambiamenti solo in VISIBILE nelle parti terminali dell'uscita -g CMD.

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

Si potrebbe provare reflex .

Reflex è un piccolo strumento per guardare una directory e rieseguire un comando quando alcuni file cambiano. È ottimo per eseguire automaticamente attività di compilazione/lint/test e per ricaricare l'applicazione quando il codice cambia.

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

Ho un GIST per questo e l'uso è piuttosto semplice

watchfiles <cmd> <paths...>

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

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

Come pochi altri hanno fatto, ho anche scritto un leggero strumento a riga di comando per fare questo. È completamente documentato, testato e modulare.

Watch-Do

Installazione

È possibile installarlo (se si dispone di Python3 e pip) utilizzando:

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

Uso

Utilizzarlo subito eseguendolo:

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

Caratteristiche generali

  • Supporta il “globbing” dei file (usare -w '*.py' o -w '**/*.py')
  • Eseguire più comandi su un cambio di file (specificare di nuovo il flag -d)
  • Mantiene dinamicamente la lista dei file da guardare se si usa il “globbing” (-r per accenderlo)
  • Più modi per “guardare” un file:
  • Tempo di modifica (default)
  • File hash
  • Trivial per implementare il proprio (questo è il ModificationTime watcher)
  • Design modulare. Se si vogliono far eseguire i comandi, quando si accede ad un file, è banale scrivere il proprio watcher (meccanismo che determina se gli esecutori devono essere eseguiti).
1
1
1
2016-02-26 19:10:05 +0000

Uso questo script per farlo. Sto usando inotify in monitor-mode

#!/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
``` ```
Usage: runatwrite.sh myfile.sh

Salva questo come runatwrite.sh

&001 &001

eseguirà myfile.sh ad ogni scrittura.

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

Ho scritto un programma Python per fare esattamente questo, chiamato rerun .

UPDATE: Questa risposta è uno script Python che fa sondaggi per i cambiamenti, che è utile in alcune circostanze. Per uno script Bash solo per Linux che usa inotify, vedi la mia altra risposta, cerca in questa pagina ‘rerun2’.

Installa per Python2 o Python3 con:

pip install --user rerun

e l'uso è molto semplice:

rerun "COMMAND"
``` &001 


Il comando è previsto come un singolo argomento, non una sequenza di args separati dallo spazio. Quindi citarlo come mostrato, il che riduce qualsiasi fuga extra che si dovrebbe aggiungere. Basta digitare il comando come lo si sarebbe digitato alla riga di comando, ma circondato da virgolette. 


Per default guarda tutti i file nella o sotto la directory corrente, saltando cose come dirs di controllo dei sorgenti conosciuti, .git, .svn, ecc. 


I flag opzionali includono '-i NAME' che ignora le modifiche ai file o alle directory con nome. Questo può essere dato più volte. 


Poiché è uno script Python, deve eseguire il comando come sotto-processo, e noi usiamo una nuova istanza della shell corrente dell'utente per interpretare 'COMMAND' e decidere quale processo eseguire effettivamente. Tuttavia, se il comando contiene alias di shell e simili definiti in .bashrc, questi non saranno caricati dalla sottostruttura. Per risolvere questo problema, si può dare una ripetizione del flag '-I', per usare sottogusci interattivi (aka 'login'). Questo è più lento e più soggetto a errori rispetto all'avvio di una shell normale, perché deve generare il vostro .bashrc. 

Lo uso con Python 3, ma l'ultima volta che ho controllato la ripetizione funzionava ancora con Python 2. 


Double-edged sword è che usa il polling invece di inotificare. Il lato positivo è che funziona su tutti i sistemi operativi. Inoltre, è meglio di alcune altre soluzioni qui mostrate in termini di esecuzione del comando dato solo una volta per un gruppo di modifiche al filesystem, non una volta per ogni file modificato, mentre allo stesso tempo esegue il comando una seconda volta se qualche file cambia di nuovo mentre il comando è in esecuzione. 


Sul lato negativo, il polling significa che c'è una latenza da 0.0 a 1.0 secondi, e naturalmente è lento a monitorare directory estremamente grandi. Detto questo, non mi sono mai imbattuto in un progetto così grande da poterlo notare fintanto che si usa '-i' per ignorare grandi cose come il virtualenv e i nodi\_moduli. 

Hmmm. `rerun` mi è stato indispensabile per anni - in pratica lo uso otto ore al giorno per eseguire test, ricostruire i file di punti mentre li modifico, ecc. Ma ora che sono arrivato a digitare questo qui, è chiaro che devo passare a una soluzione che utilizza inotify (non uso più Windows o OSX.) e che è scritta in Bash (quindi funziona con gli alias senza alcuna manipolazione extra).
1
1
1
2015-09-01 04:53:52 +0000

Una risposta oneliner che sto usando per tenere traccia di un cambio di file:

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

Non è necessario inizializzare BF se si sa che la prima data è l'ora di inizio.

Questo è semplice e portatile. Qui c'è un'altra risposta basata sulla stessa strategia che utilizza uno script. Date un'occhiata anche.


Utilizzo: Lo sto usando per il debug e per tenere d'occhio ~/.kde/share/config/plasma-desktop-appletsrc; che per qualche ragione sconosciuta continua a perdere il mio SwitchTabsOnHover=false

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

Per coloro che usano OS X, è possibile utilizzare un LaunchAgent per guardare un percorso/file per i cambiamenti e fare qualcosa quando questo accade. FYI - LaunchControl è una buona app per fare/modificare/rimuovere facilmente demoni e agenti.

esempio tratto da qui )

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

Se non volete installare nulla di nuovo per questo, ecco un piccolo script a conchiglia che potete inserire nel vostro percorso (ad esempio sotto $HOME/bin). Esegue un comando quando uno o più file forniti vengono modificati. Per esempio:

$ 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
$ onchange "!!" *.txt

It tars, e poi ha il contenuto dei file e/o delle directory, quindi non verrà eseguito ogni volta che si preme compulsivamente CTRL-S (o si digita :w), ma solo una volta che qualcosa cambia effettivamente. Notate che controllerà ogni secondo, quindi non includete molto o la vostra macchina potrebbe diventare lenta. Se volete che funzioni ad ogni salvataggio, usate stat al posto di md5sum (vedi commento). Inoltre, per mac md5 si chiama !! se ricordo bene.

Piccolo trucco: nel momento in cui volete usarlo, probabilmente vorrete ripetere l'ultimo comando che avete appena eseguito, ma più e più volte. Potete usare la scorciatoia &007 per ‘iniettare’ l'ultimo comando in questo:

&001

0
0
0
2018-04-12 18:32:28 +0000

Descrizione

Sorveglierà un file per le modifiche ed eseguirà qualsiasi comando (inclusi ulteriori argomenti) è stato dato come seconda istruzione. Cancella la schermata e stampa l'ora dell'ultima esecuzione. Nota: si può rendere la funzione più (o meno) reattiva cambiando il numero di secondi in cui la funzione dovrebbe dormire dopo ogni ciclo di loop.

Esempio di utilizzo

watch_file my_file.php php my_file.php
``` ```
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
}

Questa linea guarderà un file php my_file.php ed eseguirà l'interprete php ogni volta che cambia.

Definizione della funzione

&001

Credito

Questa è fondamentalmente una versione più generale della risposta del VDR.

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

Utilizzo di base

Ecco una soluzione che non richiede l'installazione di altro software e funziona fuori dagli schemi.

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


Questo comando esce alle seguenti condizioni: 


- Una riga viene aggiunta a `myfile.txt` dopo che il comando è stato eseguito 
- Il `myfile.txt` viene sostituito con un altro dopo che il comando è stato eseguito 


Si dice che si sta utilizzando vim, e vim sostituirà il file in fase di salvataggio. Ho testato che questo funziona con vim. 


Potete ignorare l'output di questo comando, potrebbe menzionare qualcosa come: 


> tail: myfile.txt" è stato sostituito; in seguito alla fine del nuovo file 


# Uso avanzato 


Si può combinare questo con `timeout` per restituire vero o falso. Si può usare così: 


> timeout 5s bash -c 'tail -q --follow=nome pipe 2\> /dev/null | head -n 0' && echo changed |||| echo timeout 


# Discussione 


`tail` usa `inotify` sotto il cappuccio. E' così che si ottiene un comportamento asincrono di fantasia senza alcun sondaggio. Probabilmente c'è qualche altro programma standard unix che usa `inotify` di cui possiamo abusare in modo più elegante. 


A volte questi comandi escono subito, ma se li si esegue immediatamente una seconda volta allora funzionano come pubblicizzato. Ho fatto un errore fuori campo da qualche parte, per favore aiutatemi a correggerlo. 


Su RHEL posso usare: 


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

Ma non sono sicuro che sia portatile.
0
0
0
2019-01-28 16:51:42 +0000

find può fare il trucco.

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

find -ctime 1s stampa il nome del file se è stato modificato nell'ultimo 1s.

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

Per le persone che trovano questo da Googling per le modifiche ad un particolare file, la risposta è molto più semplice (ispirata alla risposta di Gilles ).

Se si vuole fare qualcosa dopo che un particolare file è stato scritto, ecco come:

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

Salva questo come, per esempio, copy_myfile.sh e metti il file .sh nella cartella /etc/init.d/ per farlo girare all'avvio.

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

La mia situazione era leggermente diversa. Ma credo che questo possa essere utile a chi legge questa domanda.

Avevo bisogno di essere avvisato quando un file di log cambiava dimensione, ma non era necessario immediatamente. E potrebbe trattarsi di giorni o settimane in futuro, quindi non potevo usare inotify (che comunque non era installato/attivato su quel server) sulla riga di comando (non volevo usare nohup o simili). Così ho deciso di eseguire uno script bash su cron per controllare

Lo script scrive la dimensione del file guardato in un file di testo e su ogni esecuzione di cron controlla, se quel valore è cambiato e mi invia l'ultima riga se è cambiato

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

Controlla https://github.com/watchexec/watchexec .

watchexec è un semplice strumento autonomo che guarda un percorso ed esegue un comando ogni volta che rileva modifiche.

Esempio

Guarda tutti i file JavaScript, CSS e HTML nella directory corrente e tutte le sottodirectory per le modifiche, eseguendo make quando viene rilevata una modifica:

$ watchexec --exts js,css,html make

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

Lo strumento ‘fido’ può essere un'altra opzione per questa esigenza. Vedere https://www.joedog.org/fido-home/