I have two similar commands, both written as zsh functions but easily adaptable to shell scripts, other shells, etc.
# wait on a path and do something on change, e.g. `wait_do test/ run_tests.sh`
wait_do() {
local watch_file=${1}
shift
if [[ ! -e ${watch_file} ]]; then
echo "${watch_file} does not exist!"
return 1
fi
if [[ `uname` == 'Linux' ]] && ! command -v inotifywait &>/dev/null; then
echo "inotifywait not found!"
return 1
elif [[ `uname` == 'Darwin' ]] && ! command -v fswatch &>/dev/null; then
echo "fswatch not found, install via 'brew install fswatch'"
return 1
fi
local exclude_list="(\.cargo-lock|\.coverage$|\.git|\.hypothesis|\.mypy_cache|\.pgconf*|\.pyc$|__pycache__|\.pytest_cache|\.log$|^tags$|./target*|\.tox|\.yaml$)"
if [[ `uname` == 'Linux' ]]; then
while inotifywait -re close_write --excludei ${exclude_list} ${watch_file}; do
local start=$(\date +%s)
echo "start: $(date)"
echo "exec: ${@}"
${@}
local stop=$(\date +%s)
echo "finished: $(date) ($((${stop} - ${start})) seconds elapsed)"
done
elif [[ `uname` == 'Darwin' ]]; then
fswatch --one-per-batch --recursive --exclude ${exclude_list} --extended --insensitive ${watch_file} | (
while read -r modified_path; do
local start=$(\date +%s)
echo "changed: ${modified_path}"
echo "start: $(date)"
echo "exec: ${@}"
${@}
local stop=$(\date +%s)
echo "finished: $(date) ($((${stop} - ${start})) seconds elapsed)"
done
)
fi
}
# keep running a command until successful (i.e. zero return code), e.g. `nevergonnagiveyouup rsync ~/folder/ remote:~/folder/`
nevergonnagiveyouup() {
false
while [ $? -ne 0 ]; do
${@}
if [ $? -ne 0 ]; then
echo "[$(\date +%Y.%m.%d_%H%M)] FAIL: trying again in 60 seconds..."
sleep 60
false
fi
done
}