I am trying to come up with a nice and easy way of detecting when there has not been any write activity in a folder I'd like to watch.
Basically, what I'd like to have is something like this:
#!/bin/sh
# colored red text (error)
function errorMsg () {
echo '\033[0;31m'"$1"'\033[0m'
}
# check for folder to monitor argument
if [ "$#" -ne 1 ]
then
errorMsg "add experiment (folder) to monitor for activity!"
exit
fi
# time out time, 3 minutes
TIMEOUT_TIME=180
LAST_CHECKED=0
function refreshTimer () {
# when called, check seconds since epoch
CURRENT_TIME=date +%s
if [ CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ]
then
echo "file write activity halted!" | mail -s "trouble!" "[email protected]"
fi
LAST_CHECKED=date +%s
}
# set last checked to now.
LAST_CHECKED=date +%s
# start monitoring for file changes, and update timer when new files are being written.
fswatch -r ${1} | refreshTimer
but all sorts of bash magic is required I presume, since fswatch is a background task and piping its output creates a subshell. I would also be in need of some timer logic... I was thinking something like a setTimeout of which the time argument keeps being added to when there IS activity, but I don't know how to write it all in one script.
Bash, Python, Ruby, anything that can be installed using homebrew on OSX is fine but the simpler the better (so I understand what is happening).
Try the following - note that it requires
bash:fswatchindefinitely outputs lines to stdout, which must be read line by line to take timely action on new output.fswatch -0terminates lines with NULs (zero bytes), whichreadthen reads on be one by setting the line delimiter (separator) to an empty string (-d '').< <(fswatch -r -0 "${1}")provides input via stdin<to thewhileloop, wherereadconsumes the stdin input one NUL-terminated line at a time.<(fswatch -r -0 "${1}")is a process substitution that forms an "ad-hoc file" (technically, a FIFO or named file descriptor) from the output produced byfswatch -r -0 "${1}"(which watches folder${1}'s subtree (-r) for file changes, and reports each terminated with NUL (-0)).fswatchcommand runs indefinitely, the "ad-hoc file" will continue to provide input, although typically only intermittently, depending on filesystem activity.readcommand receives a new line within the timeout period (-t $TIMEOUT_TIME), it terminates successfully (exit code 0), causing the body of the loop to be executed and thenreadto be invoked again.readinvocation starts over with the timeout period.readterminates unsuccessfully - with a nonzero exit code indicating failure, which causes thewhileloop to terminate.readcommand times out.As for your original code:
Note: Some of the problems discussed could have been detected with the help of shellecheck.net
echo '\033[0;31m'"$1"'\033[0m'printfis the better choice when it comes to interpreting escape sequences, sinceecho's behavior varies across shells and platforms; for instance, running your script withbashwill not interpret them (unless you also add the-eoption).function refreshTimer ()functionsyntax is nonstandard (not POSIX-compliant), so you shouldn't use it with ashshebang line (that's what chepner meant in his comment). On OSX, you can get away with it, becausebashacts asshand most bashisms are still available when run assh, but it wouldn't work on other systems. If you know you'll be running withbashanyway, it's better to use abashshebang line.CURRENT_TIME=date +%sCURRENT_TIME=$(date +%s)(the older syntax with backticks -CURRENT_TIME=`date +%s`- works too, but has disadvantages).[ CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ]>in[ ... ]and bash's[[ ... ]]conditionals is lexical comparison and the variable names must be$-prefixed; you'd have to use an arithmetic conditional with your syntax:(( CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ))fswatch -r ${1} | refreshTimerrefreshTimerwill not get called until the pipe fills up (the timing of which you won't be able to predict), because you make no attempt to read line by line.refreshTimerwon't be preserved, becauserefreshTimerruns in a new subshell every time, due to use of a pipeline (|). Inbash, this problem is frequently worked around by providing input via a process substitution (<(...)), which you can see in action in my code above.