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


434

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 lassen und schließen werde, wenn ich mit der Arbeit an dieser Datei fertig bin.

Derzeit verwende ich Folgendes:

while read; do ./myfile.py ; done

Und dann muss ich zu diesem Terminal gehen und drücken Enter, wann immer ich diese Datei in meinem Editor speichere. Was ich will, ist ungefähr so:

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

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

Übrigens: Ich verwende Vim und weiß, dass ich einen automatischen Befehl hinzufügen kann, um etwas auf BufWrite auszuführen, aber dies ist nicht die Art von Lösung, die ich jetzt möchte.

Update: Ich möchte etwas einfaches, wenn möglich wegwerfbares. Außerdem möchte ich, dass etwas in einem Terminal ausgeführt wird, weil ich die Programmausgabe sehen möchte (ich möchte Fehlermeldungen sehen).

Über die Antworten: Danke für all deine Antworten! Sie sind alle sehr gut und gehen alle ganz anders vor als die anderen. Da ich nur eines akzeptieren muss, akzeptiere ich das, das ich tatsächlich verwendet habe (es war einfach, schnell und leicht zu merken), obwohl ich weiß, dass es nicht das eleganteste ist.


Mögliche Cross - Site - Duplikat: stackoverflow.com/questions/2972765/... (obwohl hier sie auf Thema ist =))
Ciro Santilli新疆改造中心法轮功六四事件

Ich habe vor einem Cross-Site-Duplikat verwiesen und es wurde verweigert: S;)
Francisco Tapia

4
Die Lösung von Jonathan Hartley baut auf anderen Lösungen auf und behebt große Probleme, die die am häufigsten gewählten Antworten haben: Fehlen einiger Änderungen und Ineffizienz. Bitte ändern Sie die akzeptierte Antwort auf his, die auch auf github unter github.com/tartley/rerun2 gepflegt wird (oder auf eine andere Lösung ohne diese Fehler)
nealmcb

Antworten:


404

Einfach mit inotifywait (Installiere das inotify-toolsPaket deiner 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 jedoch einen erheblichen Nachteil: Es werden Änderungen übersehen, die ausgeführt werden, während inotifywaites nicht ausgeführt wird (insbesondere, während myfilees ausgeführt wird). Das zweite Snippet hat diesen Defekt nicht. Beachten Sie jedoch, dass der Dateiname kein Leerzeichen enthält. Wenn dies ein Problem ist, verwenden Sie die --formatOption, um die Ausgabe so zu ändern, dass der Dateiname nicht mehr enthalten ist:

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

In beiden Fällen gibt es eine Einschränkung: Wenn ein Programm myfile.pydurch eine andere Datei ersetzt, anstatt in die vorhandene zu schreiben myfile, inotifywaitstirbt es. Viele Redakteure arbeiten so.

Verwenden Sie inotifywaitfür das Verzeichnis Folgendes, um diese Einschränkung zu umgehen:

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

Verwenden Sie alternativ ein anderes Tool, das dieselbe zugrunde liegende Funktionalität verwendet, z. B. incron (mit dem Sie Ereignisse registrieren können, wenn eine Datei geändert wird) oder fswatch (ein Tool, das auch für viele andere Unix-Varianten unter Verwendung der Analoga jeder Variante von Linux inotify funktioniert).


46
Ich habe alle diese (mit ganz wenigen bash Tricks) in einer einfach zu bedienende verkapselte sleep_until_modified.shSkript, abrufbar unter: bitbucket.org/denilsonsa/small_scripts/src
Denilson Sá Maia

14
while sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; doneist fantastisch. Danke.
Rhys Ulerich

5
inotifywait -e delete_selfscheint für mich gut zu funktionieren.
Kos

3
Es ist einfach, hat aber zwei wichtige Probleme: Ereignisse können übersehen werden (alle Ereignisse in der Schleife) und die Initialisierung von inotifywait wird jedes Mal durchgeführt, wodurch diese Lösung für große rekursive Ordner langsamer wird.
Wernight

6
Aus irgendeinem Grund wird while inotifywait -e close_write myfile.py; do ./myfile.py; doneimmer beendet, ohne den Befehl auszuführen (bash und zsh). Damit dies funktioniert, musste ich zum || trueBeispiel while inotifywait -e close_write myfile.py || true; do ./myfile.py; done
Folgendes

166

entr ( http://entrproject.org/ ) bietet eine benutzerfreundlichere Oberfläche zum Inotifizieren (und unterstützt auch * BSD und Mac OS X).

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

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

Ich habe es in meinem gesamten Projektquellbaum verwendet, um die Komponententests für den Code auszuführen, den ich gerade ändere, und es hat meinen Workflow bereits enorm verbessert.

Flags wie -c(Löschen des Bildschirms zwischen den Durchläufen) und -d(Beenden, wenn eine neue Datei zu einem überwachten Verzeichnis hinzugefügt wird) bieten noch mehr Flexibilität. Sie können beispielsweise Folgendes tun:

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

Ab Anfang 2018 befindet es sich noch in der aktiven Entwicklung und ist in Debian & Ubuntu ( apt install entr) zu finden. Das Bauen aus dem Repo des Autors war auf jeden Fall schmerzfrei.


3
Behandelt keine neuen Dateien und deren Änderungen.
Wernight

2
@Wernight - ab 7. Mai 2014 hat entr die neue -dFlagge; Es ist etwas langatmiger, aber Sie können while sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; donemit neuen Dateien umgehen.
Paul Fenney

1
entr ist auch in debian repos verfügbar, zumindest ab debian jessie / 8.2 am ...
Peter V. Mørch

5
Das beste, das ich unter OS X gefunden habe. fswatch ergreift zu viele flippige Ereignisse und ich möchte nicht die Zeit verbringen, um herauszufinden, warum
dtc

5
Es ist erwähnenswert, dass entr auf Homebrew verfügbar ist, also brew install entrwird es wie erwartet
funktionieren

108

Ich schrieb ein Python - Programm zu tun genau dies genannt , wenn-verändert .

Die Verwendung ist einfach:

when-changed FILE COMMAND...

Oder um mehrere Dateien anzusehen:

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

FILEkann ein Verzeichnis sein. Beobachten Sie rekursiv mit -r. Verwenden Sie %fdiese Option , um den Dateinamen an den Befehl zu übergeben.


1
@ysangkok ja, in der neuesten Version des Codes :)
joh

4
Jetzt verfügbar bei "pip install when-changed". Funktioniert immer noch gut. Vielen Dank.
AL Flanagan

2
Zum Löschen des Bildschirms können Sie zuerst verwenden when-changed FILE 'clear; COMMAND'.
Dave James Miller

1
Diese Antwort ist viel besser, weil ich sie auch unter Windows machen kann. Und dieser Typ hat tatsächlich ein Programm geschrieben, um die Antwort zu bekommen.
Wolfpack'08,

4
Gute Nachrichten, Leute! when-changedist jetzt plattformübergreifend! Schauen Sie sich die neueste Version 0.3.0 an :)
joh

52

Wie wäre es mit diesem Skript? Er verwendet den statBefehl, um die Zugriffszeit einer Datei abzurufen, und führt einen Befehl aus, wenn sich die Zugriffszeit ändert (wenn auf eine Datei zugegriffen wird).

#!/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
Wäre statdie geänderte Zeit nicht eine bessere Antwort, wenn sich eine Datei ändert?
Xen2050

1
Würde das Ausführen von stat viele Male pro Sekunde viele Lesevorgänge auf der Festplatte verursachen? oder würde der systemaufruf von fstat diese antworten irgendwie automatisch zwischenspeichern lassen? Ich versuche, eine Art 'Grunzuhr' zu schreiben, um meinen C-Code zu kompilieren, wenn ich Änderungen vornehme
Oskenso Kashi

Dies ist gut, wenn Sie den Dateinamen kennen, der im Voraus überwacht werden soll. Besser wäre es, den Dateinamen an das Skript zu übergeben. Besser wäre es noch, wenn Sie viele Dateinamen übergeben könnten (zB "mywatch * .py"). Besser wäre es, wenn Dateien in Unterverzeichnissen ebenfalls rekursiv verarbeitet werden könnten, wie dies bei einigen anderen Lösungen der Fall ist.
Jonathan Hartley

5
Nur für den Fall, dass sich jemand über hohe Lesezugriffe wundert, habe ich dieses Skript in Ubuntu 17.04 mit einem Ruhezustand von 0,05 Sekunden getestet und vmstat -dnach Festplattenzugriffen Ausschau gehalten. Es scheint, dass Linux einen fantastischen Job beim
Cachen solcher

Es gibt Tippfehler in "BEFEHL", ich habe versucht zu beheben, aber SO sagt "Bearbeiten sollte nicht weniger als 6 Zeichen sein"
user337085

30

Lösung mit Vim:

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

Aber ich möchte diese Lösung nicht, weil es etwas nervig ist, etwas zu tippen, es ist ein bisschen schwierig, sich genau zu merken, was zu tippen ist, und es ist ein bisschen schwierig, seine Effekte rückgängig zu machen (müssen ausgeführt werden :au! BufWritePost myfile.py). Außerdem blockiert diese Lösung Vim, bis der Befehl ausgeführt wurde.

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

Entfernen Sie den :silentBefehl , um die Programmausgabe anzuzeigen (und Ihren Bearbeitungsablauf vollständig zu unterbrechen, da die Ausgabe einige Sekunden lang über Ihren Editor schreibt, bis Sie die Eingabetaste drücken) .


1
Dies kann in Kombination mit entr(siehe unten) ganz nett sein - lassen Sie vim einfach eine Dummy-Datei berühren, die entr gerade betrachtet, und den Rest im Hintergrund erledigen ... oder tmux send-keyswenn Sie sich in einer solchen Umgebung befinden :)
Paul Fenney

nett! Sie können ein Makro für Ihre .vimrcDatei erstellen
ErichBSchulz

23

Wenn Sie zufällig npminstalliert haben, nodemonist dies wahrscheinlich der einfachste Weg, um loszulegen, insbesondere unter OS X, das anscheinend keine inotify-Tools hat. Es unterstützt das Ausführen eines Befehls, wenn sich ein Ordner ändert.


5
Es werden jedoch nur .js- und .coffee-Dateien überwacht.
Zelk

6
Die aktuelle Version scheint jeden Befehl zu unterstützen, zum Beispiel: nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models
kek

1
Ich wünschte, ich hätte mehr Informationen, aber osx hat eine Methode, um Änderungen zu verfolgen, fsevents
ConstantineK

1
Unter OS X können Sie Launch Daemons auch mit einem WatchPathsSchlüssel verwenden, wie in meinem Link gezeigt.
Adam Johns

19

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

watch -d -t -g ls -lR

Dieser Befehl wird beendet, wenn sich die Ausgabe ändert. Er ls -lRlistet alle Dateien und Verzeichnisse mit Größe und Datum auf. Wenn also eine Datei geändert wird, sollte er den Befehl beenden, wie man sagt:

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

Ich weiß, dass diese Antwort von niemandem gelesen werden kann, aber ich hoffe, dass jemand darauf eingehen würde.

Befehlszeilenbeispiel:

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

Öffnen Sie ein anderes Terminal:

~ $ echo "testing" > /tmp/test

Nun wird das erste Terminal ausgegeben 1,2,3

Einfaches Skript-Beispiel:

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

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

5
Netter Hack. Ich habe getestet und es scheint ein Problem zu geben, wenn die Auflistung lang ist und die geänderte Datei außerhalb des Bildschirms liegt. Eine kleine Änderung könnte etwa so aussehen: watch -d -t -g "ls -lR tmp | sha1sum"
Atle

3
Wenn Sie Ihre Lösung jede Sekunde beobachten, funktioniert sie für immer und führt MY_COMMAND nur aus, wenn sich einige Dateien ändern: watch -n1 "watch -d -t -gls -lR && MY_COMMAND"
mnesarco

Meine Version von watch (unter Linux watch from procps-ng 3.3.10) akzeptiert Float-Sekunden als Intervall und fragt daher watch -n0.2 ...jede fünfte Sekunde ab. Gut für gesunde Tests in Einheiten unter einer Millisekunde.
Jonathan Hartley

15

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' auf Ihrem PATH und rufen Sie sie auf mit:

rerun COMMAND

Es wird BEFEHL jedes Mal ausgeführt, wenn sich ein Dateisystemänderungsereignis in Ihrem aktuellen Verzeichnis befindet (rekursiv).

Dinge, die man daran mögen könnte:

  • Es verwendet inotify und reagiert daher schneller als Polling. Hervorragend für die Ausführung von Unit-Tests im Millisekundenbereich oder das Rendern von Grafik-Dot-Dateien, wenn Sie auf "Speichern" klicken.
  • Weil es so schnell ist, müssen Sie nicht sagen, dass große Unterverzeichnisse (wie node_modules) nur aus Leistungsgründen ignoriert werden sollen.
  • Es ist besonders reaktionsschnell, da es beim Start nur einmal inotifywait aufruft, anstatt es auszuführen, und bei jeder Iteration den teuren Hit des Erstellens von Uhren verursacht.
  • Es sind nur 12 Zeilen Bash
  • Da es sich um Bash handelt, interpretiert es Befehle, die Sie übergeben, genau so, als hätten Sie sie an einer Bash-Eingabeaufforderung eingegeben. (Vermutlich ist dies weniger cool, wenn Sie eine andere Shell verwenden.)
  • Im Gegensatz zu den meisten anderen inotify-Lösungen auf dieser Seite gehen keine Ereignisse verloren, die während der Ausführung von COMMAND auftreten.
  • Beim ersten Ereignis wird eine "Totzeit" von 0,15 Sekunden eingegeben, während der andere Ereignisse ignoriert werden, bevor COMMAND genau einmal ausgeführt wird. Dies ist so, dass die Flut von Ereignissen, die durch den Tanz zum Erstellen, Schreiben und Verschieben verursacht werden, den Vi oder Emacs beim Speichern eines Puffers ausführen, nicht zu mehreren mühsamen Ausführungen einer möglicherweise langsam laufenden Testsuite führen. Alle Ereignisse, die dann während der Ausführung von COMMAND auftreten, werden nicht ignoriert. Sie verursachen eine zweite Totzeit und die anschließende Ausführung.

Dinge, die man daran nicht mögen könnte:

  • Es verwendet inotify, funktioniert also nicht außerhalb von Linuxland.
  • Da inotify verwendet wird, wird nicht versucht, Verzeichnisse zu überwachen, die mehr Dateien als die maximale Anzahl von inotify-Überwachungen durch Benutzer enthalten. Standardmäßig scheint dies auf verschiedenen Computern, die ich verwende, auf etwa 5.000 bis 8.000 eingestellt zu sein, ist aber leicht zu erhöhen. Siehe https://unix.stackexchange.com/questions/13751/kernel-inotify-watch-limit-reached
  • Befehle, die Bash-Aliase enthalten, können nicht ausgeführt werden. Ich könnte schwören, dass dies funktioniert hat. Im Prinzip würde ich davon ausgehen, dass dies funktioniert, da dies Bash ist und COMMAND nicht in einer Subshell ausgeführt wird. Ich würde gerne hören, wenn jemand weiß, warum es nicht. Viele der anderen Lösungen auf dieser Seite können solche Befehle ebenfalls nicht ausführen.
  • Persönlich wünschte ich, ich könnte eine Taste in dem Terminal drücken, in dem es läuft, um manuell eine zusätzliche Ausführung von COMMAND auszulösen. Könnte ich das irgendwie einfach hinzufügen? Eine gleichzeitig ausgeführte 'while read -n1'-Schleife, die auch die Ausführung aufruft?
  • Im Moment habe ich es so programmiert, dass es das Terminal löscht und den ausgeführten BEFEHL bei jeder Iteration ausgibt. Einige Leute mögen vielleicht Befehlszeilen-Flags hinzufügen, um solche Dinge auszuschalten, usw. Aber dies würde die Größe und Komplexität um ein Vielfaches erhöhen.

Dies ist eine Verfeinerung von @ cychois Antwort.


2
Ich glaube, Sie sollten "$@"statt verwenden $@, um ordnungsgemäß mit Argumenten zu arbeiten, die Leerzeichen enthalten. Gleichzeitig verwenden Sie jedoch eval, was den Benutzer von Rerun dazu zwingt, beim Zitieren besonders vorsichtig zu sein.
Denilson Sá Maia

Vielen Dank, Denilson. Könnten Sie ein Beispiel dafür geben, wo das Zitieren sorgfältig durchgeführt werden muss? Ich habe es die letzten 24 Stunden benutzt und bisher keine Probleme mit Leerzeichen gesehen oder sorgfältig irgendetwas zitiert - nur aufgerufen als rerun 'command'. Sind Sie nur sagen , dass , wenn ich „$ @“ verwendet, dann könnte der Benutzer aufrufen , wie rerun commanddie nicht so nützlich für mich scheint (ohne Anführungszeichen?): Ich in der Regel nicht wollen , Bash zu tun , jede Verarbeitung des Befehls , bevor es vorbei zu wiederholen. Wenn der Befehl zB "echo $ myvar" enthält, möchte ich die neuen Werte von myvar in jeder Iteration sehen.
Jonathan Hartley

1
So etwas rerun foo "Some File"könnte zerbrechen. Aber da Sie verwenden eval, kann es als umgeschrieben werden rerun 'foo "Some File". Beachten Sie, dass bei der Pfaderweiterung manchmal Leerzeichen eingefügt werden: rerun touch *.fooDiese werden wahrscheinlich unterbrochen, und bei der Verwendung rerun 'touch *.foo'wird eine geringfügig andere Semantik verwendet (die Pfaderweiterung erfolgt nur einmal oder mehrmals).
Denilson Sá Maia

Danke für die Hilfe. Ja: rerun ls "some file"bricht wegen der Leerzeichen. rerun touch *.foo*Funktioniert normalerweise einwandfrei, schlägt jedoch fehl, wenn die mit * .foo übereinstimmenden Dateinamen Leerzeichen enthalten. Vielen Dank, dass Sie mir dabei geholfen haben, herauszufinden, wie rerun 'touch *.foo'unterschiedliche Semantiken vorliegen, aber ich vermute, dass die Version mit einfachen Anführungszeichen die von mir gewünschte Semantik ist: Ich möchte, dass jede Wiederholungsiteration so funktioniert, als ob ich den Befehl erneut eingetippt hätte - daher möchte *.foo ich sie bei jeder Iteration erweitern . Ich werde Ihre Vorschläge versuchen, um ihre Auswirkungen zu untersuchen ...
Jonathan Hartley

Weitere Diskussionen zu dieser PR ( github.com/tartley/rerun2/pull/1 ) und anderen.
Jonathan Hartley

12

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

  1. Nimmt zwei Argumente auf: die zu überwachende Datei und einen Befehl (ggf. mit Argumenten)
  2. Kopiert die überwachte Datei in das Verzeichnis / tmp
  3. Überprüft alle zwei Sekunden, ob die überwachte Datei neuer als die Kopie ist
  4. Wenn es neuer ist, überschreibt es die Kopie mit dem neueren Original und führt den Befehl aus
  5. Bereinigt sich selbst, wenn Sie Strg-C drücken

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

Dies funktioniert unter FreeBSD. Das einzige Portabilitätsproblem, an das ich denken kann, 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.


9
Polling ist die einzige tragbare Methode, aber die meisten Systeme verfügen über einen Benachrichtigungsmechanismus für Dateiänderungen (inotify unter Linux, kqueue unter FreeBSD, ...). Sie haben dabei ein schwerwiegendes Anführungsproblem $cmd, aber glücklicherweise ist dies leicht zu beheben: Lassen Sie die cmdVariable los und führen Sie sie aus "$@". Ihr Skript ist nicht für die Überwachung einer großen Datei geeignet. Dies kann jedoch durch Ersetzen cpdurch behoben werden touch -r(Sie benötigen nur das Datum, nicht den Inhalt). Portabilitätstechnisch -nterfordert der Test bash, ksh oder zsh.
Gilles

8

Werfen Sie einen Blick auf Incron . Es ist ähnlich wie cron, verwendet jedoch inotify-Ereignisse anstelle von time.


Dies kann funktionieren, aber das Erstellen eines Incron-Eintrags ist im Vergleich zu anderen Lösungen auf dieser Seite ein recht arbeitsintensiver Vorgang.
Jonathan Hartley

6

Eine andere Lösung mit NodeJs, fsmonitor :

  1. Installieren

    sudo npm install -g fsmonitor
    
  2. Über die Befehlszeile (Beispiel: Überwachen von Protokollen und "Einzelhandel", wenn sich eine Protokolldatei ändert)

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

Randnotiz: Das Beispiel könnte gelöst werden tail -F -q *.log, denke ich.
Volker Siegel

Es war nur ein Beispiel, tail -fnicht cleardas Terminal.
Atika

6

Sehen 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 überwacht und Befehle ausführt, wenn Änderungen auftreten. Gute Chancen, auch wenn es ein Plugin gibt, das genau das tut, was Sie versuchen.


6

Wenn Sie Nodemon installiert haben, können Sie dies tun:

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

In meinem Fall bearbeite ich HTML lokal und versende 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"

6

Unter Linux:

man watch

watch -n 2 your_command_to_run

Führt den Befehl alle 2 Sekunden aus.

Wenn Ihr Befehl länger als 2 Sekunden dauert, wartet watch, bis er ausgeführt wurde, bevor es erneut ausgeführt wird.


Das ist ziemlich einfach, obwohl es eine Verschwendung ist, es ist einfach für Entwicklungsaufgaben wie das Ändern von Stilen in Echtzeit.
Xeoncross

2
Was passiert, wenn der Befehl länger als zwei Sekunden dauert?
Dreißigstel

@thirtythreeforty Ein schnelles Experiment unter Ubuntu zeigt, dass watch die vollen zwei Sekunden wartet, egal wie lange der Befehl dauert, um ausgeführt zu werden. FWIW, die Ruhezeit kann mit '-n' bis zu einem Minimum von 0,1 Sekunden angegeben werden.
Jonathan Hartley

5

Watchdog ist ein Python-Projekt und könnte genau das sein, wonach Sie suchen:

Unterstützte Plattformen

  • Linux 2.6 (inotify)
  • Mac OS X (FSEvents, kqueue)
  • FreeBSD / BSD (kqueue)
  • Windows (ReadDirectoryChangesW mit E / A-Abschlussports; ReadDirectoryChangesW-Arbeitsthreads)
  • Betriebssystemunabhängig (Abfragen der Festplatte nach Verzeichnis-Snapshots und regelmäßiges Vergleichen; langsam und nicht empfohlen)

Habe gerade einen Kommandozeilen-Wrapper dafür geschrieben watchdog_exec:

Beispiel läuft

Führen Sie bei einem fs-Ereignis mit Dateien und Ordnern im aktuellen Verzeichnis den echo $src $dstBefehl aus, sofern das fs-Ereignis nicht geändert wurde, und führen Sie dann den python $srcBefehl aus.

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

Verwenden Sie kurze Argumente und beschränken Sie sich darauf, nur ausgeführt zu werden, wenn Ereignisse " main .py" beinhalten:

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

BEARBEITEN: Gerade gefunden Watchdog hat eine offizielle CLI angerufen watchmedo, also checkt das auch aus.


4

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

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

Alternativ können Sie ein Phony-Ziel erstellen und die Regel dafür festlegen, dass sowohl Ihr Skript aufgerufen wird als auch das Phony-Ziel berührt wird (obwohl dies immer noch von Ihrem Skript abhängt).


11
while sleep 1 ; do something ; doneist etwas besser als while true ; do something ; sleep 1 ; done. Zumindest stoppt es leicht, wenn Sie Strg + C drücken.
Denilson Sá Maia

Führt das Entfernen des Ruhezustands zu einer Besetztschleife (CPU erzeugt Wärme und beeinträchtigt die Akkulaufzeit eines Laptops)?
Steven Lu

2
@StevenLu: nein, der Schlaf ist nicht beschäftigt zu warten. Das Problem ist, dass wenn der Schlaf im Körper ist, Control-C den Schlaf beendet und die Schleife von vorne beginnt. Der Stromverbrauch zum Starten der Schleife ist unbedeutend. Probieren Sie es selbst in einem Terminal. Sie müssen Control-C gedrückt halten, damit es funktioniert, wenn Sie im Körper schlafen.
Janus Troelsen

Richtig. Ich glaube, ich habe es verpasst und nicht gesehen, dass der Schlaf immer noch als Schleifenbedingung vorliegt. Das kleine Tweak ist ziemlich genial.
Steven Lu

4

2
Hierbei handelt es sich um ein funktionsreiches Bash-Skript mit 200 Zeilen, statdas die angegebenen Dateinamen abfragt, in md5sumder Ausgabe ausgeführt und den angegebenen Befehl erneut ausführt, wenn sich dieser Wert ändert. Da es sich um Bash handelt, ist es meiner Meinung nach eine gute Aufgabe, den angegebenen Befehl so auszuführen, als ob Sie ihn an einer Bash-Eingabeaufforderung eingegeben hätten. (Im Gegensatz dazu können die meisten der hier in anderen Sprachen geschriebenen Lösungen keine Befehle ausführen, die beispielsweise Shell-Aliase enthalten wie ll)
Jonathan Hartley

4

Gilles 'Antwort wurde verbessert .

Diese Version wird inotifywaiteinmal ausgeführt und überwacht anschließend Ereignisse (z. B. modify). Dies inotifywait muss nicht bei jedem aufgetretenen Ereignis erneut ausgeführt werden.

Es geht schnell und schnell! (Auch wenn große Verzeichnisse rekursiv überwacht werden)

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

Dies ist die beste Antwort auf der Seite für Linux-Benutzer. Ersetzen Sie den Inhalt der Schleife durch 'execute $ @', und der Benutzer kann dieses Skript aufrufen, indem er seinen eigenen Befehl zum Ausführen übergibt. Es funktioniert sogar mit Befehlen, die Shell-Aliase enthalten, wenn Sie sie mit etwas wie ". Scriptname COMMAND" als Quelle angeben. Dies findet immer noch den Skriptnamen auf dem PFAD.
Jonathan Hartley,

Ich denke du willst 'während des Lesens REPLY' setzen?
Jonathan Hartley,

1
Danke für die Klarstellung. Vielen Dank für die Einstellung! Ich hätte diese Kommentare gelöscht, aber jetzt natürlich nicht.
Jonathan Hartley

3

Ein bisschen mehr auf der Programmierseite, aber Sie möchten so etwas wie inotify . Es gibt Implementierungen in vielen Sprachen, z. B. jnotify und pyinotify .

Mit dieser Bibliothek können Sie einzelne Dateien oder ganze Verzeichnisse überwachen und Ereignisse zurückgeben, wenn eine Aktion erkannt wird. Die zurückgegebenen Informationen umfassen unter anderem den Dateinamen, die Aktion (Erstellen, Ändern, Umbenennen, Löschen) und den Dateipfad.


3

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

/usr/ports/sysutils/wait_on

3

Ich mag die Einfachheit, while inotifywait ...; do ...; doneaber es gibt zwei Probleme:

  • Dateiänderungen, die während der auftreten do ...;, werden übersehen
  • Langsam bei Verwendung im rekursiven Modus

Aus diesem Grund habe ich ein Hilfsskript erstellt , das inotifywait ohne diese Einschränkungen verwendet: inotifyexec

Ich schlage vor, dass Sie dieses Skript in Ihren Pfad einfügen, wie in ~/bin/. Die Verwendung wird nur durch Ausführen des Befehls beschrieben.

Beispiel: inotifyexec "echo test" -r .


Das Skript wurde aktualisiert, um den Regex-Mustervergleich zu unterstützen.
Wernight

Beide Probleme werden durch die Verwendung von inotifywait im "--monitor" -Modus gelöst. Siehe die Antwort von cychoi.
Jonathan Hartley

3

Verbesserte Sebastian-Lösung mit watchBefehl:

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 seien Sie vorsichtig: Der watchBefehl hat bekannte Fehler (siehe man): Er reagiert nur auf Änderungen in VISIBLE in Terminal-Teilen der -g CMDAusgabe.


2

Du könntest es mit Reflex versuchen .

Reflex ist ein kleines Tool, mit dem Sie ein Verzeichnis überwachen und einen Befehl erneut ausführen können, wenn sich bestimmte Dateien ändern. Es eignet sich hervorragend zum automatischen Ausführen von Kompilierungs- / Flusen- / Testaufgaben und zum erneuten Laden Ihrer Anwendung, wenn sich der Code ändert.

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

Können Sie ein wenig über das Tool zitieren / erklären? Lesen Sie kurz, wie Sie Software zur Anleitung empfehlen können .
Bertieb

1

Eine Online-Antwort, mit der ich eine Dateiänderung nachverfolge:

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

Sie müssen BF nicht initialisieren, wenn Sie wissen, dass das erste Datum die Startzeit ist.

Das ist einfach und portabel. Es gibt eine andere Antwort, die auf der gleichen Strategie basiert und hier ein Skript verwendet. Schauen Sie auch mal rein.


Verwendung: Ich benutze dies, um Fehler zu beheben und ein Auge darauf zu haben ~/.kde/share/config/plasma-desktop-appletsrc. dass aus irgendeinem unbekannten Grund meine immer wieder verliertSwitchTabsOnHover=false


1

Ich benutze dieses Skript, um es zu tun. 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

Bei jedem Schreibvorgang wird myfile.sh ausgeführt.


1

Für Benutzer von OS X können Sie einen LaunchAgent verwenden, um einen Pfad / eine Datei auf Änderungen zu überwachen und in diesem Fall Maßnahmen zu ergreifen. FYI - LaunchControl ist eine gute App zum einfachen Erstellen / Ändern / Entfernen von Daemons / Agents.

( Beispiel von hier genommen )

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

Für Leute, die dies durch Googeln nach Änderungen an einer bestimmten Datei finden, ist die Antwort viel einfacher (inspiriert von Gilles 'Antwort ).

Wenn Sie etwas tun möchten, nachdem in eine bestimmte Datei geschrieben wurde, 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 beispielsweise als copy_myfile.shund legen Sie die .shDatei in den /etc/init.d/Ordner, damit sie beim Start ausgeführt wird.


Teilt das Problem mit Giles 'Antwort, dass es bei jeder Iteration inotifywait ausgeführt wird, was für das rekursive Überwachen sehr großer Verzeichnisse unreagierbar sein kann. Lesen Sie die Antwort von cychoi, um das Problem zu beheben.
Jonathan Hartley


0

Wie einige andere bereits geschrieben haben, habe ich auch ein einfaches Befehlszeilentool geschrieben, um dies zu tun. Es ist vollständig dokumentiert, getestet und modular.

Watch-Do

Installation

Du kannst es installieren (wenn du Python3 und pip hast) mit:

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

Verwendungszweck

Verwenden Sie es sofort, indem Sie Folgendes ausführen:

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

Funktionsübersicht

  • Unterstützt File Globbing (benutze -w '*.py'oder -w '**/*.py')
  • Führen Sie mehrere Befehle für eine Dateiänderung aus (geben Sie einfach das -dFlag erneut an)
  • Bewahrt die Liste der zu überwachenden Dateien dynamisch auf, wenn Globbing verwendet wird ( -rum dies zu aktivieren )
  • Mehrere Möglichkeiten zum "Ansehen" einer Datei:
    • Änderungszeit (Standard)
    • Datei-Hash
    • Trivial, um Ihre eigenen zu implementieren (dies ist der ModificationTime- Watcher)
  • Modulares Design. Wenn Sie möchten, dass Befehle ausgeführt werden, wenn auf eine Datei zugegriffen wird, ist es trivial, einen eigenen Watcher zu schreiben (Mechanismus, der festlegt, ob die Macher ausgeführt werden sollen).
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.