Ich möchte ein Shell-Skript ausführen, wenn sich eine bestimmte Datei oder ein bestimmtes Verzeichnis ändert.
Wie kann ich das leicht machen?
Verwenden Sie inotify-tools .
Ich verwende dieses Skript, um ein Build-Skript für Änderungen in einer Verzeichnisstruktur auszuführen:
#!/bin/bash -eu
DIRECTORY_TO_OBSERVE="js" # might want to change this
function block_for_change {
inotifywait --recursive \
--event modify,move,create,delete \
$DIRECTORY_TO_OBSERVE
}
BUILD_SCRIPT=build.sh # might want to change this too
function build {
bash $BUILD_SCRIPT
}
build
while block_for_change; do
build
done
Verwendet inotify-tools
. Überprüfen Sie auf inotifywait
man page , wie Sie anpassen können, was den Build auslöst.
Sie können versuchen, mit entr
tool beliebige Befehle auszuführen, wenn sich Dateien ändern. Beispiel für Dateien:
$ ls -d * | entr sh -c 'make && make test'
oder:
$ ls *.css *.html | entr reload-browser Firefox
Für Verzeichnisse verwenden Sie -d
, aber Sie müssen es in der Schleife verwenden, z.
while true; do find path/ | entr -d echo Changed; done
oder:
while true; do ls path/* | entr -pd echo Changed; done
Checken Sie den Monitor-Daemon des Kernel-Dateisystems aus
http://freshmeat.net/projects/kfsmd/
Hier ist eine Anleitung:
Wie gesagt, inotify-tools ist wahrscheinlich die beste Idee. Wenn Sie jedoch zum Spaß programmieren, können Sie versuchen, Hacker-XPs durch sinnvolle Anwendung von tail -f zu verdienen.
Hier ist eine weitere Option: http://fileschanged.sourceforge.net/
Siehe insbesondere "Beispiel 4", das "ein Verzeichnis überwacht und neue oder geänderte Dateien archiviert".
Wie wäre es mit diesem Skript? Verwendet den Befehl 'stat', um die Zugriffszeit einer Datei abzurufen, und führt einen Befehl aus, wenn sich die Zugriffszeit ändert (wenn auf die Datei zugegriffen wird).
#!/bin/bash
while true
do
ATIME=`stat -c %Z /path/to/the/file.txt`
if [[ "$ATIME" != "$LTIME" ]]
then
echo "RUN COMMNAD"
LTIME=$ATIME
fi
sleep 5
done
inotifywait
kann Sie zufrieden stellen.
Hier ist ein allgemeines Beispiel dafür:
inotifywait -m /path -e create -e moved_to -e close_write | # -m is --monitor, -e is --event
while read path action file; do
if [[ "$file" =~ .*rst$ ]]; then # if suffix is '.rst'
echo ${path}${file} ': '${action} # execute your command
echo 'make html'
make html
fi
done
Nur zum Debuggen: Wenn ich ein Shell-Skript schreibe und beim Speichern ausführen möchte, verwende ich Folgendes:
#!/bin/bash
file="$1" # Name of file
command="${*:2}" # Command to run on change (takes rest of line)
t1="$(ls --full-time $file | awk '{ print $7 }')" # Get latest save time
while true
do
t2="$(ls --full-time $file | awk '{ print $7 }')" # Compare to new save time
if [ "$t1" != "$t2" ];then t1="$t2"; $command; fi # If different, run command
sleep 0.5
done
Führen Sie es als aus
run_on_save.sh myfile.sh ./myfile.sh arg1 arg2 arg3
Edit: Oben auf Ubuntu 12.04 getestet, für Mac OS, ändern Sie die ls-Zeilen in:
"$(ls -lT $file | awk '{ print $8 }')"
Fügen Sie ~/.bashrc folgendes hinzu:
function react() {
if [ -z "$1" -o -z "$2" ]; then
echo "Usage: react <[./]file-to-watch> <[./]action> <to> <take>"
Elif ! [ -r "$1" ]; then
echo "Can't react to $1, permission denied"
else
TARGET="$1"; shift
ACTION="[email protected]"
while sleep 1; do
ATIME=$(stat -c %Z "$TARGET")
if [[ "$ATIME" != "${LTIME:-}" ]]; then
LTIME=$ATIME
$ACTION
fi
done
fi
}
Beispiel für die Verwendung von inotifywait
:
Angenommen, ich möchte Rails test
jedes Mal ausführen, wenn ich eine relevante Datei ändere.
1. Erstellen Sie eine Liste der relevanten Dateien, die Sie ansehen möchten:
Sie könnten dies manuell tun, aber ich finde ack
sehr hilfreich, um diese Liste zu erstellen.
ack --type-add=Rails:ext:rb,erb --Rails -f > Inotifyfile
2. Bitten Sie inotifywait
, die Arbeit zu erledigen
while inotifywait --fromfile Inotifyfile; do Rails test; done
Das ist es!
ANMERKUNG: Falls Sie Vagrant verwenden, um den Code auf einer VM auszuführen, ist möglicherweise die Erweiterung mhallin/vagrant-notify-forwarder nützlich.
UPDATE: Besser noch eine alias
und die Datei loswerden:
alias rtest="while inotifywait $(ack --type-add=Rails:ext:rb,erb --Rails -f | tr \\n \ ); do Rails test; done"