watchfile - monitor file(s) and execute a command when files are changed
#!/bin/bash | |
version=1.0.1 | |
versionDate="2014-02-14" | |
function showHelp() { | |
echo "watchfile - monitor file(s)/command and perform action when changed | |
Possible ways of usage | |
---------------------------------------- | |
Monitor FILE, when modified execute FILE | |
\$ watchfile [options] FILE | |
Monitor FILE, when modified execute CMND [PARAM_1 ..PARAM_N] | |
\$ watchfile [options] FILE CMND [PARAM_1 .. PARAM_N] | |
Monitor FILE_1 .. FILE_N, when modified execute CMND [PARAM_1 ..PARAM_N] | |
\$ watchfile [options] -i FILE_1 .. FILE_N -e CMND [PARAM_1 .. PARAM_N] | |
Monitor output of CMND1, when modified execute CMND2 [PARAM_1 .. PARAM_N] | |
\$ watchfile [options] -s \"CMND1\" -e CMND [PARAM_1 .. PARAM_N] | |
options: | |
-h, --help Show help | |
-d, --delay=N Specify the delay between each monitor update. Default 0.5. | |
--check-content If set it checks file content, instead of just the timestamp. | |
Has no effect with the -s flag set. | |
--no-clear If set, it doesn't clear the screen before executing CMND. | |
-v, --version Outputs version information | |
flags: | |
-s, Next argument specifies monitor command. Requires -e flag. | |
-i, Start listing files to monitor. Requires -e flag. | |
-e, Start listing command to execute. Requires -s or -i or flag. | |
Must be the last flag used (CMND can thus use flags as parameters) | |
Note: If CMND isn't found, and ./CMND is, it automatically uses this command. | |
Note: If the command uses ampersands (&, &&), these must be escaped (\&, \&\&). | |
Examples | |
---------------------------------------- | |
Monitor executable foo.sh, and execute on change | |
$ watchfile foo.sh | |
Monitor python file foo.py, and execute it on change | |
$ watchfile foo.py python foo.py | |
As above, but monitor content (not just timestamp): | |
$ watchfile --check-content foo.py python foo.py | |
Compiling main.cpp file on change: | |
$ watchfile main.cpp g++ -Wall main.cpp -o main | |
Compiling main.cpp file on change, running when compilation succeedes: | |
$ watchfile main.cpp g++ -Wall main.cpp -o main \&\& ./main | |
Compiling project whenever source files changes, and running if it succeedes: | |
$ watchfile -s \"find . -name '*.cpp' -or -name '*.h' | xargs cat\" \\ | |
-e make \&\& ./main | |
See: http://swarminglogic.com/jotting/2014_02_watchfile for more examples | |
Mainted at: https://gist.github.com/swarminglogic/8963507 | |
Author: Roald Fernandez (github@swarminglogic.com) | |
Version: $version ($versionDate) | |
License: CC-zero (public domain) | |
" | |
exit $1 | |
} | |
function parseParameters() { | |
tmp=$@ | |
leftovers="" | |
while test $# -gt 0; do | |
case "$1" in | |
-h|--help) | |
showHelp 0 | |
;; | |
--no-clear) | |
shift | |
flagNoClear=true | |
;; | |
--check-content) | |
shift | |
flagCheckContent=true | |
;; | |
-d) | |
shift | |
delay=$1 | |
shift | |
;; | |
--delay*) | |
delay=`echo $1 | sed -e 's/^[^=]*=//g'` | |
shift | |
;; | |
-v|--version) | |
shift | |
echo "watchfile $version" | |
exit 0 | |
;; | |
-s) | |
shift | |
flagS=true | |
watchcmnd=$1 | |
shift | |
;; | |
-i) | |
shift | |
flagI=true | |
nI=0 | |
for i in `seq 1 $#`; do | |
if [[ ${!i} == -* ]] ; then | |
break; | |
else | |
((++nI)) | |
fi | |
done | |
watchfiles=${@:1:$nI} | |
shift $nI | |
;; | |
-e) | |
shift | |
flagE=true | |
execcmnd=${@:1} | |
break | |
;; | |
-*) | |
leftovers="$leftovers "$1 | |
shift | |
;; | |
*) | |
leftovers="$leftovers "$1 | |
shift | |
;; | |
esac | |
done | |
if [[ $flagE && (! $flagS) && (! $flagI) ]] ; then | |
echo "Error: If -e flag is set, -s or -i flags are required." | |
exit 1 | |
elif [[ ($flagS || $flagI) && ! $flagE ]] ; then | |
echo "Error: If -s or -i flags are set, the -e flags is required." | |
exit 1 | |
elif [[ $flagS && $flagI ]]; then | |
echo "Error: Both -s and -i flags cannot be used simultaneously." | |
exit 1 | |
elif [[ (! $flagE) && (! $flagS) && (! $flagI) ]] ; then | |
set -- $leftovers | |
watchfiles=$1 | |
if [ $# -gt 1 ]; then | |
execcmnd=${@:2} | |
else | |
execcmnd=$watchfiles | |
fi | |
fi | |
} | |
# Exit with help if no parameters | |
if [[ ! $@ ]] ; then showHelp 1; fi | |
# Defaults | |
delay=0.5 | |
# Parse parameters into $watch and $execcmnd variables | |
parseParameters "$@" | |
# Sanitize executable | |
set -- $execcmnd | |
if [[ ! `which $1` ]] && [[ -x ./$1 ]] ; then | |
execcmnd=./$execcmnd | |
elif [[ ! `which $1` ]] && [[ ! -x ./$1 ]] ; then | |
echo "Error: No executable $1 or ./$1 found" | |
exit 1 | |
fi | |
# Main monitoring loop. | |
if [[ -z $watchcmnd ]] ; then | |
if [[ ! $flagCheckContent ]] ; then | |
watchcmnd="stat -c %Y $watchfiles | md5sum" | |
else | |
watchcmnd="cat $watchfiles | md5sum" | |
fi | |
else | |
watchcmnd="$watchcmnd | md5sum" | |
fi | |
md5sum=`eval $watchcmnd` | |
md5sumNow=$md5sum | |
while [[ true ]] | |
do | |
# Loop until some files have changed | |
while [[ "$md5sumNow" = "$md5sum" ]] | |
do | |
sleep $delay | |
md5sumNow=`eval $watchcmnd` | |
done | |
# Execute the file, as it has changed. | |
if [[ ! $flagNoClear ]] ; then | |
clear | |
fi | |
eval $execcmnd | |
md5sum=$md5sumNow | |
done |
This comment has been minimized.
This comment has been minimized.
Can it be used to monitor and compile CSS files, for e.g compile stylus files to css ? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Just changed it a little bit to make it work on OSX https://gist.github.com/thinkxl/3e45880b7d27fc0b1d6d