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.

Denilson Sá Maia
quelle
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).

Gilles
quelle
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.

Paul Fenney
quelle
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.

joh
quelle
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
VDR
quelle
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) .

Denilson Sá Maia
quelle
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.

davidtbernal
quelle
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}
Sebastian
quelle
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.

Jonathan Hartley
quelle
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.

MikeyMike
quelle
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.

Florian Diesch
quelle
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"
    
Atika
quelle
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.

Wouter Van Vliet
quelle
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 [email protected]:/var/www" -e ".html"
Jay
quelle
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.

Eric Leschinski
quelle
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.

Samuel Marks
quelle
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).

ctgPi
quelle
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

swarminglogic hat ein Skript namens watchfile.sh geschrieben , das auch als GitHub Gist verfügbar ist .

Denilson Sá Maia
quelle
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
cychoi
quelle
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.

John T
quelle
3

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

/usr/ports/sysutils/wait_on
akond
quelle
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 .

Wernight
quelle
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.

alex_1948511
quelle
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
masterxilo
quelle
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

Dr. Beco
quelle
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.

Fernando Silva
quelle
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>
Hefewe1zen
quelle
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.

LondonRob
quelle
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

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

David Ramirez
quelle
Bitte lesen Sie Wie empfehle ich Software für einige Tipps, wie Sie Software empfehlen sollten. Sie sollten mindestens einen Link, einige zusätzliche Informationen zur Software selbst und Informationen zur Behebung des Problems in der Frage bereitstellen.
DavidPostill
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).
Vimist
quelle