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

Wie führe ich einen Befehl aus, wenn sich eine Datei ändert?

Ich möchte eine schnelle und einfache Möglichkeit, einen Befehl auszuführen, wenn sich eine Datei ändert. Ich möchte etwas sehr Einfaches, etwas, das ich auf einem Terminal laufen lasse und das ich schließe, sobald ich mit der Datei fertig bin.

Zur Zeit benutze ich folgendes:

while read; do ./myfile.py ; done

Und dann muss ich zu diesem Terminal gehen und die Eingabetaste drücken, wann immer ich diese Datei in meinem Editor speichere. Was ich will, ist etwas wie dies:

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

Oder irgendeine andere Lösung, die so einfach ist.

Übrigens: Ich benutze Vim, und ich weiß, dass ich einen Autobefehl hinzufügen kann, um etwas auf BufWrite laufen zu lassen, aber dies ist nicht die Art von Lösung, die ich jetzt will.

Aktualisieren: Ich will etwas Einfaches, das nach Möglichkeit verworfen werden kann. Außerdem möchte ich etwas, das in einem Terminal läuft, weil ich die Programmausgabe sehen möchte (ich möchte Fehlermeldungen sehen).

Über die Antworten: Danke für all Ihre Antworten! Sie sind alle sehr gut, und jede von ihnen verfolgt einen ganz anderen Ansatz als die anderen. Da ich nur eine akzeptieren muss, akzeptiere ich diejenige, die ich tatsächlich verwendet habe (sie war einfach, schnell und leicht zu merken), auch wenn ich weiß, dass sie nicht die eleganteste ist.

Antworten (37)

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

Einfach, mit inotifywait (installieren Sie das Paket inotify-tools Ihrer Distribution):

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

oder

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

Das erste Snippet ist einfacher, hat aber einen erheblichen Nachteil: es verpasst Änderungen, die durchgeführt werden, während inotifywait nicht läuft (insbesondere, während myfile läuft). Das zweite Snippet hat diesen Defekt nicht. Beachten Sie jedoch, dass es davon ausgeht, dass der Dateiname kein Leerzeichen enthält. Wenn das ein Problem ist, verwenden Sie die Option --format, um die Ausgabe so zu ändern, dass der Dateiname nicht enthalten ist:

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

So oder so, es gibt eine Einschränkung: Wenn irgendein Programm myfile.py durch eine andere Datei ersetzt, anstatt in die bestehende Datei myfile zu schreiben, stirbt inotifywait. Viele Editoren arbeiten auf diese Weise.

Um diese Beschränkung zu überwinden, verwenden Sie inotifywait im Verzeichnis:

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

Alternativ dazu können Sie ein anderes Tool verwenden, das die gleiche zugrundeliegende Funktionalität verwendet, wie incron (damit können Sie Ereignisse registrieren, wenn eine Datei geändert wird) oder fswatch (ein Tool, das auch auf vielen anderen Unix-Varianten funktioniert, wobei jede Variante das Analogon des inotify von Linux verwendet).

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

entr http://entrproject.org/ ) bietet eine freundlichere Schnittstelle zum Inotifizieren (und unterstützt auch *BSD & Mac OS X).

Es macht es sehr einfach, mehrere zu überwachende Dateien anzugeben (nur durch ulimit -n begrenzt), nimmt den Ärger mit zu ersetzenden Dateien weg und erfordert weniger Bash-Syntax:

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

Ich habe es in meinem gesamten Projekt-Quellcodebaum benutzt, um die Unit-Tests für den Code, den ich gerade modifiziere, durchzuführen, und es hat meinen Arbeitsablauf bereits enorm verbessert. Flags wie

Flags wie -c (Bildschirm zwischen den Durchläufen löschen) und -d (Beenden, wenn eine neue Datei zu einem überwachten Verzeichnis hinzugefügt wird) fügen noch mehr Flexibilität hinzu, z.B. können Sie:

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

Anfang 2018 befindet es sich noch in aktiver Entwicklung und kann in Debian & Ubuntu (apt install entr) gefunden werden; das Bauen aus dem Repo des Autors war in jedem Fall schmerzfrei.

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

Ich habe ein Python-Programm geschrieben, um genau dies zu tun: when-changed .

Die Verwendung ist einfach:

when-changed FILE COMMAND...

Oder um mehrere Dateien zu überwachen:

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

FILE kann ein Verzeichnis sein. Mit -r können Sie rekursiv beobachten. Verwenden Sie %f, um den Dateinamen an den Befehl zu übergeben.

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

Wie wäre es mit diesem Skript? Es verwendet den Befehl stat, um die Zugriffszeit einer Datei zu erhalten, und führt einen Befehl aus, wenn sich die Zugriffszeit ändert (bei jedem Dateizugriff).

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

Lösung mit Vim:

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

Aber ich will diese Lösung nicht, weil es irgendwie nervig ist, zu tippen, es ist ein bisschen schwierig, sich zu merken, was genau zu tippen ist, und es ist ein bisschen schwierig, seine Auswirkungen rückgängig zu machen (Notwendigkeit, :au! BufWritePost myfile.py auszuführen). Zusätzlich blockiert diese Lösung Vim, bis der Befehl fertig ausgeführt ist.

Ich habe diese Lösung hier nur der Vollständigkeit halber hinzugefügt, da sie anderen Leuten helfen könnte.

Um die Programmausgabe anzuzeigen (und Ihren Editierfluss komplett zu unterbrechen, da die Ausgabe für ein paar Sekunden über Ihren Editor schreibt, bis Sie die Eingabetaste drücken), entfernen Sie den Befehl :silent.

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

Wenn Sie zufällig npm installiert haben, ist nodemon wahrscheinlich der einfachste Weg, um anzufangen, besonders unter OS X, das anscheinend keine inotify-Tools hat. Es unterstützt die Ausführung eines Befehls, wenn sich ein Ordner ändert.

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

Für diejenigen, die inotify-tools nicht installieren können wie ich, sollte dies nützlich sein:

watch -d -t -g ls -lR

Dieser Befehl wird beendet, wenn sich die Ausgabe ändert, ls -lR listet jede Datei und jedes Verzeichnis mit Größe und Datum auf, wenn also eine Datei geändert wird, sollte der Befehl beendet werden, wie man sagt:

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

Ich weiß, dass diese Antwort vielleicht von niemandem gelesen wird, aber ich hoffe, dass jemand danach greift.

Befehlszeilenbeispiel:

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

Öffnen Sie ein weiteres Terminal:

~ $ echo "testing" > /tmp/test

Nun wird das erste Terminal 1,2,3

ausgeben Einfaches Skriptbeispiel:

#!/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 on github ) ist ein 10-zeiliges Bash-Skript der Form:

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

Speichern Sie die Github-Version als ‘rerun’ in Ihrem PATH und rufen Sie sie auf mit:

rerun COMMAND

Es führt jedes Mal COMMAND aus, wenn es ein Dateisystem-Modify-Ereignis in Ihrem aktuellen Verzeichnis gibt (rekursiv. )

Dinge, die einem gefallen könnten:

  • Es benutzt inotify, ist also reaktionsfähiger als Polling. Hervorragend geeignet für die Ausführung von Unit-Tests im Sub-Millisekundenbereich oder für das Rendern von graphviz-Punktdateien, jedesmal, wenn Sie auf ‘Speichern’ klicken.
  • Weil es so schnell ist, brauchen Sie sich nicht die Mühe zu machen, ihm zu sagen, er solle große Unterverzeichnisse (wie node_modules) nur aus Leistungsgründen ignorieren.
  • Es ist extra super reaktionsschnell, weil es inotifywait nur einmal beim Start aufruft, anstatt es laufen zu lassen, und den teuren Treffer beim Einrichten von Uhren bei jeder Iteration in Kauf nimmt.
  • Es sind nur 12 Zeilen Bash
  • Weil es die Bash ist, interpretiert es Befehle, die Sie ihm übergeben, genau so, als hätten Sie sie an einer Bash-Eingabeaufforderung eingegeben. (Vermutlich ist das weniger cool, wenn Sie eine andere Shell verwenden.)
  • Es verliert keine Ereignisse, die während der Ausführung von COMMAND passieren, im Gegensatz zu den meisten anderen inotify-Lösungen auf dieser Seite.
  • Beim ersten Ereignis tritt es für 0,15 Sekunden in eine ‘tote Periode’ ein, während der andere Ereignisse ignoriert werden, bevor COMMAND genau einmal ausgeführt wird. Dies geschieht, damit die durch den “create-write-move”-Tanz, den Vi oder Emacs beim Speichern eines Puffers ausführt, verursachte Hektik der Ereignisse nicht zu mehrfachen mühsamen Ausführungen einer möglicherweise langsam laufenden Testsuite führt. Alle Ereignisse, die dann während der Ausführung von COMMAND auftreten, werden nicht ignoriert - sie führen zu einer zweiten Totzeit und anschließender Ausführung.

Dinge, die man daran missfallen könnten:

  • Es benutzt inotify, also wird es außerhalb von Linuxland nicht funktionieren.
  • Weil es inotify benutzt, wird es kotzen, wenn es versucht, Verzeichnisse zu überwachen, die mehr Dateien enthalten, als die maximale Anzahl von Benutzer inotify überwacht. Standardmäßig scheint dies auf verschiedenen Rechnern, die ich benutze, auf etwa 5.000 bis 8.000 eingestellt zu sein, aber es ist leicht zu erhöhen. Siehe https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Befehle, die Bash-Aliase enthalten, werden nicht ausgeführt. Ich könnte schwören, dass das mal funktioniert hat. Da es sich hier um die Bash handelt und nicht um die Ausführung von COMMAND in einer Subshell, würde ich im Prinzip erwarten, dass es funktioniert. Ich würde gerne hören, ob jemand weiß, warum es nicht funktioniert. Viele der anderen Lösungen auf dieser Seite können solche Befehle auch nicht ausführen.
  • Ich persönlich wünschte, ich könnte eine Taste in dem Terminal, in dem es läuft, drücken, um manuell eine zusätzliche Ausführung von COMMAND zu bewirken. Könnte ich dies einfach irgendwie hinzufügen? Eine gleichzeitig laufende ‘while read -n1’-Schleife, die auch execute aufruft?
  • Im Moment habe ich sie so kodiert, daß sie das Terminal löscht und den ausgeführten COMMAND bei jeder Iteration ausgibt. Einige Leute möchten vielleicht Kommandozeilen-Flags hinzufügen, um solche Dinge abzuschalten, usw. Aber das würde die Größe und Komplexität um ein Vielfaches erhöhen.

Dies ist eine Verfeinerung von @cychois Antwort.

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

Hier ist ein einfaches Shell-Bourne-Shell-Skript, das:

  1. Benötigt zwei Argumente: die zu überwachende Datei und einen Befehl (ggf. mit Argumenten)
  2. Kopiert die Datei, die Sie überwachen, in das Verzeichnis /tmp
  3. Prüft alle zwei Sekunden, ob die Datei, die Sie überwachen, neuer ist als die Kopie
  4. Wenn sie neuer ist, überschreibt sie die Kopie mit dem neueren Original und führt den Befehl aus 5. räumt nach sich selbst auf, wenn Sie Ctr-C drücken

Dies funktioniert unter FreeBSD. Das einzige Portabilitätsproblem, das mir einfällt, ist, wenn ein anderes Unix den Befehl mktemp(1) nicht hat, aber in diesem Fall können Sie den Namen der temporären Datei einfach hart codieren.

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

wenn Sie nodemon installiert haben, dann können Sie dies tun:

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

In meinem Fall bearbeite ich html lokal und schicke es an meinen Remote-Server, wenn sich eine Datei ändert.

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

Werfen Sie einen Blick auf incron . Es ist ähnlich wie cron, verwendet aber inotifizierte Ereignisse anstelle von Zeit.

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

Schauen Sie sich Guard an, insbesondere mit diesem Plugin: https://github.com/hawx/guard-shell

Sie können es so einrichten, dass es eine beliebige Anzahl von Mustern im Verzeichnis Ihres Projekts beobachtet und Befehle ausführt, wenn Änderungen auftreten. Die Chancen stehen gut, dass es sogar ein Plugin für das gibt, was Sie überhaupt zu tun versuchen.

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

Eine andere Lösung mit NodeJs, * fsmonitor ** :

  1. Installieren

  2. Von der Befehlszeile aus (Beispiel: Protokolle überwachen und “retail”, wenn sich eine Protokolldatei ändert)

5
5
5
2015-12-28 10:44:18 +0000
  • [ Watchdog ** ist ein Python-Projekt, und ist vielleicht genau das, wonach Sie suchen:

Unterstützte Plattformen

  • Linux 2. 6 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD/BSD (kqueue)
  • Windows (ReadDirectoryChangesW mit I/O-Vervollständigungs-Ports; ReadDirectoryChangesW-Worker-Threads)
  • OS-unabhängig (Abfrage der Platte nach Verzeichnis-Snapshots und periodischer Vergleich dieser Snapshots; langsam und nicht empfohlen)

hat dafür einfach einen Kommandozeilen-Wrapper geschrieben * watchdog_exec **:

Beispiel führt

Beim fs-Ereignis, das Dateien und Ordner im aktuellen Verzeichnis betrifft, Befehl echo $src $dst ausführen, es sei denn, das fs-Ereignis wird geändert, dann Befehl python $src ausführen.

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

Verwendung kurzer Argumente und Beschränkung auf die Ausführung nur bei Ereignissen mit “ Haupt.py”:

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

BEARBEITEN: Watchdog hat eine offizielle CLI namens watchmedo, also überprüfen Sie das auch.

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

Unter Linux:

man watch

watch -n 2 your_command_to_run

Der Befehl wird alle 2 Sekunden ausgeführt.

Wenn die Ausführung Ihres Befehls mehr als 2 Sekunden dauert, wartet watch, bis er fertig ist, bevor er erneut ausgeführt wird.

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

Wenn Ihr Programm irgendeine Art von Protokoll/Ausgabe erzeugt, können Sie ein Makefile mit einer Regel für dieses Protokoll/diese Ausgabe erstellen, die von Ihrem Skript abhängt, und etwas wie

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

tun. Alternativ können Sie ein gefälschtes Ziel erstellen und die Regel dafür sowohl Ihr Skript aufrufen als auch das gefälschte Ziel berühren lassen (während es immer noch von Ihrem Skript abhängt).

4
4
4
2014-09-15 23:24:58 +0000
4
4
4
2015-06-17 18:03:51 +0000

Verbessert auf Gilles’ Antwort .

Diese Version lässt inotifywait einmal laufen und überwacht danach auf Ereignisse (.z.B.: modify). So dass inotifywait nicht bei jedem auftretenden Ereignis erneut ausgeführt werden muss.

Das geht schnell und schnell!(auch bei rekursiver Überwachung großer Verzeichnisse)

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

Etwas mehr auf der Programmierseite, aber Sie wollen so etwas wie inotify . Es gibt Implementierungen in vielen Sprachen, wie z.B. jnotify und pyinotify .

Diese Bibliothek ermöglicht Ihnen die Überwachung einzelner Dateien oder ganzer Verzeichnisse und gibt Ereignisse zurück, wenn eine Aktion entdeckt wird. Die zurückgegebenen Informationen umfassen neben anderen nützlichen Informationen den Dateinamen, die Aktion (Erstellen, Ändern, Umbenennen, Löschen) und den Dateipfad.

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

Für diejenigen unter Ihnen, die nach einer FreeBSD-Lösung suchen, hier ist der Port:

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

Mir gefällt die Einfachheit von while inotifywait ...; do ...; done, aber es hat zwei Probleme:

  • Dateiänderungen während do ...; werden verpasst
  • langsam bei Verwendung im rekursiven Modus

Dafür habe ich ein Hilfsskript erstellt, das inotifywait ohne diese Einschränkungen verwendet: inotifyexec

Ich schlage vor, Sie setzen dieses Skript in Ihren Pfad, wie in ~/bin/. Die Verwendung wird beschrieben, indem Sie einfach den Befehl ausführen.

Beispiel: inotifyexec "echo test" -r .

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

Verbessert Sebastians Lösung mit dem Befehl 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

Aufrufbeispiel:

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

Es funktioniert, aber Vorsicht: Der Befehl watch hat bekannte Fehler (siehe man): er reagiert nur auf Änderungen in VISIBLE in Terminalteilen der Ausgabe von -g CMD.

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

Sie können reflex versuchen.

Reflex ist ein kleines Tool, um ein Verzeichnis zu überwachen und einen Befehl erneut auszuführen, wenn sich bestimmte Dateien ändern. Es eignet sich hervorragend zum automatischen Ausführen von Kompilier/Lint/Test-Aufgaben und zum erneuten Laden Ihrer Anwendung, wenn sich der Code ändert.

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

Ich habe eine GIST dafür und die Verwendung ist ziemlich einfach

watchfiles <cmd> <paths...>

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

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

Wie einige andere habe auch ich ein leichtgewichtiges Kommandozeilen-Tool geschrieben, um dies zu tun. Es ist vollständig dokumentiert, getestet und modular aufgebaut.

Watch-Do

Installation

Sie können es (wenn Sie Python3 und pip haben) mit Hilfe von:

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

Verwendung

Benutzen Sie es direkt, indem Sie es ausführen:

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

Funktionsübersicht

  • Unterstützt Datei-Globbing (verwenden Sie -w '*.py' oder -w '**/*.py')
  • Führen Sie mehrere Befehle bei einer Dateiänderung aus (geben Sie einfach das Flag -d erneut an)
  • Verwaltet dynamisch die Liste der zu überwachenden Dateien, wenn Globbing verwendet wird (-r zum Einschalten)
  • Mehrere Möglichkeiten, eine Datei zu “überwachen”:
  • Änderungszeit (Voreinstellung)
  • Datei-Hash
  • Trivial, um Ihre eigene zu implementieren (dies ist der ModificationTime Watcher)
  • Modularer Aufbau. Wenn Sie Befehle ausführen lassen wollen, wenn auf eine Datei zugegriffen wird, ist es trivial, einen eigenen Watcher zu schreiben (Mechanismus, der bestimmt, ob die Macher ausgeführt werden sollen).
1
1
1
2016-02-26 19:10:05 +0000

Dazu benutze ich dieses Skript. Ich benutze inotify im Monitor-Modus

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

Speichern Sie dies als runatwrite.sh

Usage: runatwrite.sh myfile.sh

es wird myfile.sh bei jedem Schreiben ausführen.

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

Ich habe ein Python-Programm geschrieben, um genau dies zu tun, es heißt rerun .

UPDATE: Diese Antwort ist ein Python-Skript, das nach Änderungen fragt, was unter bestimmten Umständen nützlich ist. Für ein reines Linux-Bash-Skript, das inotify verwendet, siehe meine andere Antwort, suchen Sie auf dieser Seite nach ‘rerun2’.

Installieren für Python2 oder Python3 mit:

pip install --user rerun

und die Benutzung ist sehr einfach:

rerun "COMMAND"

Der Befehl wird als ein einzelnes arg erwartet, nicht als eine Folge von durch Leerzeichen getrennten Args. Daher zitieren Sie ihn wie gezeigt, was jedes zusätzliche Escaping, das Sie hinzufügen müssten, reduziert. Geben Sie den Befehl einfach so ein, wie Sie ihn in der Kommandozeile eingegeben hätten, aber umgeben von Anführungszeichen.

Standardmäßig überwacht es alle Dateien im oder unter dem aktuellen Verzeichnis und überspringt dabei Dinge wie bekannte Quellcode-Kontrollverzeichnisse, .git, .svn usw.

Optionale Flags enthalten ‘-i NAME’, das Änderungen an benannten Dateien oder Verzeichnissen ignoriert. Dies kann mehrfach angegeben werden.

Da es sich um ein Python-Skript handelt, muss der Befehl als Unterprozess ausgeführt werden, und wir verwenden eine neue Instanz der aktuellen Shell des Benutzers, um ‘COMMAND’ zu interpretieren und zu entscheiden, welcher Prozess tatsächlich ausgeführt werden soll. Wenn Ihr Befehl jedoch Shell-Aliase und ähnliches enthält, die in .bashrc definiert sind, werden diese von der Sub-Shell nicht geladen. Um dies zu beheben, können Sie rerun ein ‘-I’-Flag geben, um interaktive (alias ‘login’) Subshells zu benutzen. Dies ist langsamer und fehleranfälliger als das Starten einer regulären Shell, da sie Ihre .bashrc ausgeben muss.

Ich benutze es mit Python 3, aber das letzte Mal, als ich geprüft habe, dass die Wiederholung immer noch mit Python 2 funktioniert.

Double-edged sword ist, dass es Polling statt inotify verwendet. Auf der anderen Seite bedeutet das, dass es auf jedem Betriebssystem funktioniert. Außerdem ist es besser als einige andere hier gezeigte Lösungen, da es den gegebenen Befehl nur einmal für eine Reihe von Dateisystemänderungen ausführt, nicht einmal pro geänderter Datei, während es gleichzeitig den Befehl ein zweites Mal ausführt, wenn sich irgendwelche Dateien erneut ändern, während der Befehl ausgeführt wird.

Auf der Kehrseite bedeutet Polling, daß es eine Latenzzeit von 0,0 bis 1,0 Sekunden hat, und natürlich ist es langsam, um extrem große Verzeichnisse zu überwachen. Abgesehen davon ist mir noch nie ein Projekt begegnet, das groß genug ist, dass dies überhaupt auffällt, solange man ‘-i’ benutzt, um große Dinge wie die Virtualität und die node_module zu ignorieren.

Hmmm. rerun ist für mich seit Jahren unverzichtbar - ich benutze es im Grunde täglich acht Stunden für die Durchführung von Tests, den Neuaufbau von Punktdateien, während ich sie bearbeite, usw. Aber jetzt, wo ich das hier tippe, ist es klar, dass ich zu einer Lösung wechseln muss, die inotify verwendet (ich benutze nicht mehr Windows oder OSX.) und in Bash geschrieben ist (so dass es mit Aliasen ohne zusätzliche Fummelei funktioniert).

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

Eine Oneliner-Antwort, die ich verwende, um eine Dateiänderung zu verfolgen:

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

Sie brauchen BF nicht zu initialisieren, wenn Sie wissen, dass das erste Datum die Startzeit ist.

Dies ist einfach und portabel. Es gibt hier eine weitere Antwort, die auf derselben Strategie basiert und ein Skript verwendet. Schauen Sie auch nach.


Verwendung: Ich verwende dies, um ~/.kde/share/config/plasma-desktop-appletsrc zu debuggen und im Auge zu behalten; das verliert aus irgendeinem unbekannten Grund immer wieder meine SwitchTabsOnHover=false

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

Für diejenigen, die OS X verwenden, können Sie einen LaunchAgent verwenden, um einen Pfad/eine Datei auf Änderungen zu überwachen und etwas zu tun, wenn dies geschieht. FYI - LaunchControl ist eine gute Anwendung, um Dämonen/Agenten einfach zu erstellen/ändern/entfernen

Beispiel von hier )

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

Wenn Sie dafür nichts Neues installieren wollen, können Sie hier ein kleines Shell-Skript in Ihren Pfad eintragen (z.B. unter $HOME/bin). Es führt einen Befehl aus, wenn die bereitgestellten eine oder mehrere Dateien geändert werden. Zum Beispiel:

$ 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

Es tariert und hasht dann den Inhalt der Dateien und/oder Verzeichnisse, so dass es nicht jedes Mal ausgeführt wird, wenn Sie zwanghaft STRG-S drücken (oder :w tippen), sondern nur dann, wenn sich tatsächlich etwas ändert. Beachten Sie, dass es jede Sekunde überprüft wird, also fügen Sie nicht zu viel ein, sonst könnte Ihr Rechner langsam werden. Wenn Sie möchten, dass er bei jedem Speichern läuft, verwenden Sie stattdessen stat (siehe Kommentar). Außerdem heißt md5sum bei mac md5, wenn ich mich richtig erinnere.

Netter kleiner Trick: In dem Moment, in dem Sie es benutzen wollen, werden Sie wahrscheinlich den letzten Befehl, den Sie gerade ausgeführt haben, wiederholen wollen, aber immer und immer wieder. Sie können die Abkürzung !! verwenden, um den letzten Befehl in diesen zu ‘injizieren’:

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

Beschreibung

Hiermit wird eine Datei auf Änderungen überwacht und jeder Befehl (einschließlich weiterer Argumente) ausgeführt wurde als zweite Anweisung angegeben. Außerdem wird der Bildschirm gelöscht und die Zeit der letzten Ausführung ausgedruckt. Hinweis: Sie können die Funktion mehr (oder weniger) reaktiv machen, indem Sie die Anzahl der Sekunden ändern, die die Funktion nach jedem while-Schleifenzyklus schlafen soll.

Beispielbenutzung

watch_file my_file.php php my_file.php

Diese Zeile beobachtet eine php-Datei my_file.php und durchläuft den php-Interpreter, wenn sie sich ändert.

Funktionsdefinition

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
}

Credit

Dies ist im Grunde eine allgemeinere Version der VDR-Antwort.

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

Grundlegende Benutzung

Hier ist eine Lösung, die keine Installation weiterer Software erfordert und out of the box funktioniert.

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

Dieser Befehl wird unter den folgenden Bedingungen beendet:

  • Eine Zeile wird zu myfile.txt hinzugefügt, nachdem der Befehl ausgeführt wurde
  • Die myfile.txt wird durch eine andere ersetzt, nachdem der Befehl ausgeführt wurde

Sie sagen, Sie benutzen vim, und vim ersetzt die Datei beim Speichern. Ich habe getestet, dass dies mit vim funktioniert.

Sie können die Ausgabe dieses Befehls ignorieren, sie kann etwas erwähnen wie:

tail: ‘myfile.txt’ wurde ersetzt; folgendes Ende der neuen Datei

Erweiterte Benutzung

Sie können dies mit timeout kombinieren, um wahr oder falsch zurückzugeben. Sie können es wie folgt verwenden:

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

Diskussion

tail verwendet inotify unter der Haube. So erhalten Sie dieses schicke asynchrone Verhalten ohne jegliche Abfrage. Es gibt wahrscheinlich noch ein anderes Standard-Unix-Programm, das inotify verwendet, das wir eleganter missbrauchen können.

Manchmal werden diese Befehle sofort beendet, aber wenn Sie sie sofort ein zweites Mal ausführen, dann funktionieren sie wie angekündigt. Ich habe irgendwo einen off-by-one Fehler gemacht, bitte helfen Sie mir, diesen zu korrigieren.

Auf RHEL kann ich verwenden:

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

Aber ich bin mir nicht sicher, ob das portabel ist.

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

find kann den Trick.

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

find -ctime 1s druckt den Dateinamen, wenn er in der letzten 1s geändert wurde.

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

Für Leute, die dies finden, indem sie nach Änderungen an einer bestimmten Datei googeln, ist die Antwort viel einfacher (inspiriert von Gilles’ Antwort ).

Wenn Sie etwas nach dem Schreiben einer bestimmten Datei tun wollen, gehen Sie wie folgt vor:

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

Speichern Sie dies z.B. als copy_myfile.sh und legen Sie die Datei .sh in den Ordner /etc/init.d/, damit sie beim Start ausgeführt wird.

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

Ich hatte eine etwas andere Situation. Aber ich glaube, dass dies für jemanden, der diese Frage liest, nützlich sein könnte.

Ich musste benachrichtigt werden, wenn sich die Größe einer Protokolldatei änderte, aber nicht sofort. Und es könnte Tage oder Wochen in der Zukunft liegen, so dass ich inotify (das auf diesem Server ohnehin nicht installiert/aktiviert wurde) nicht auf der Befehlszeile verwenden konnte (ich wollte nohup oder ähnliches nicht verwenden). Also beschloss ich, ein Bash-Skript auf cron laufen zu lassen, um zu überprüfen

Das Skript schreibt die Dateigröße der überwachten Datei in eine Textdatei und prüft bei jedem Lauf von cron, ob sich dieser Wert geändert hat, und schickt mir die letzte Zeile, wenn sich

#!/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
``` geändert hat
0
0
0
2019-11-23 23:41:53 +0000

Sehen Sie sich https://github.com/watchexec/watchexec an.

watchexec ist ein einfaches, eigenständiges Tool, das einen Pfad überwacht und einen Befehl ausführt, sobald es Änderungen feststellt.

Beispiel

Überwacht alle JavaScript-, CSS- und HTML-Dateien im aktuellen Verzeichnis und allen Unterverzeichnissen auf Änderungen und führt make aus, wenn eine Änderung festgestellt wird:

$ watchexec --exts js,css,html make

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

Das “Fido”-Instrument könnte eine weitere Option für diesen Bedarf sein. Siehe https://www.joedog.org/fido-home/