Try this rough script, it follows your idea of supplying a file argument and making use of md5sum. It displays a line with a time stamp whenever there are changes based on md5sum being different, saves a log file, and stops when you ctrl-c. Contents of watch_and_notify.sh:
#!/bin/bash
logf="$1.log"
interval=2
first_run=
# temp files, current and last md5s for diff to compare
lm1="$(mktemp /tmp/lm1.$$.XXXX)"
lm2="$(mktemp /tmp/lm2.$$.XXXX)"
if [ -z "$1" ]; then
echo "No file specified, aborting" >&2
exit 1
fi
echo "Watching at ${interval}s intervals: $1"
# loop forever until cancel this script
while true; do
md5sum "$1" > $lm1
# otherwise in the first iteration,
# lm2 does not yet exist, so diff
# will always unintentionally report
# a difference when comparing existing
# file with nonexisting file
if [ -z "$first_run" ]; then
cp -a $lm1 $lm2
first_run=1
fi
# test ! to invert usual exit code
if ! diff $lm2 $lm1; then
echo -e "$(date +"%F %R")\tChange detected:\t$1" | tee -a "$logf"
fi
# rotate
mv $lm1 $lm2
sleep $interval
done
# when you ctrl-c it should garbage cleanup
trap "rm $lm1 $lm2; exit 1" SIGINT
Example
Start an empty text file named a.txt
$ touch a.txt
Run the script like this, and see:
$ ./watch_and_notify.sh a.txt
Watching at 2s intervals: a.txt
On a second terminal, you test making a change, for example
$ echo addition >> a.txt
On the first terminal running the script, you'll see an update:
Watching at 2s intervals: a.txt
1c1
< d41d8cd98f00b204e9800998ecf8427e a.txt
---
> 9913e6909c108b5c32c69280474b2b2a a.txt
2015-09-29 15:56 Change detected: a.txt
On the second terminal you again introduce another change:
$ echo anotherchange >> a.txt
Then on the first terminal running the script, the output is updated again:
Watching at 2s intervals: a.txt
1c1
< d41d8cd98f00b204e9800998ecf8427e a.txt
---
> 9913e6909c108b5c32c69280474b2b2a a.txt
2015-09-29 15:56 Change detected: a.txt
1c1
< 9913e6909c108b5c32c69280474b2b2a a.txt
---
> 5c1c20a75b9982128f8300a7940f9ce0 a.txt
2015-09-29 16:06 Change detected: a.txt
You quit with ctrl-c. and will be returned to the command prompt. You list contents and see there is a log:
$ ls -lh
total 12K
-rw-r--r-- 1 meme meme 22 Sep 29 16:06 a.txt
-rw-r--r-- 1 meme meme 80 Sep 29 16:06 a.txt.log
-rwxrwxrwx 1 meme meme 775 Sep 29 15:26 watch_and_notify.sh
Viewing the log, you see the same timestamped entries logging the moments of change:
$ cat a.txt.log
2015-09-29 15:56 Change detected: a.txt
2015-09-29 16:06 Change detected: a.txt
Code explanation
Most of the overall flow can be understood hopefully from the comments in the script, but basically it repeatedly runs an md5sum command on the file, while saving that result and rotating it so that the diff program compares current result versus previous iteration's result, if there is a difference then perform the reporting action. In this case those actions are to output to screen with timestamp as well as to append to a log. The user stops the script with ctrl-c, and is left with a log of the timestamped moments when differences were detected.
Within the script
lm1="$(mktemp /tmp/lm1.$$.XXXX)"
lm2="$(mktemp /tmp/lm2.$$.XXXX)"
- lm1 and lm2 are temp files to store md5sum outputs for comparison, generated only when our script is run
- the key approach is as you said, literally comparing md5sums, so to do that we first define somewhere temporary to store them
mktempto help make a unique temporary file name.$$is the current process id, to throw in some randomnessXXXXXtellsmktempto replace eachXwith a randomized alphanumeric characters
So when running we can check and see /tmp indeed contain at least a file named with our defined pattern:
$ ls -lh /tmp/lm*
-rw-r--r-- 1 meme meme 40 Sep 29 15:08 /tmp/lm2.8248.xGJTl
Next we have the while loop:
# loop forever until cancel this script
while true; do
...
done
The majority of the code is sandwiched within in a big while <command>; do ... done loop. Since the command/condition is true and always will be, this code runs indefinitely until we ctrl-c to stop it.
Each loop iteration starts first with generating current iteration's md5sum results, and saving it:
md5sum "$1" > $lm1
$1means the first positional argument. In this case when runwatch_and_notify.sh a.txt,$1will bea.txt> $lm1to write the output to our temp file defined earlier
When first run, there won't be a previous iteration. Yet the way the diff command is arranged it has to compare changes of the previous md5sum result $lm2 to $lm1, which, on first run, will always unintentionally show a difference, so on the first run there needed to be a special conditional action. To be able to recognize the first run, we make an initial empty variable, defined prior to the while loop:
first_run=
Then, within the loop we test for this:
if [ -z "$first_run" ]; then
cp -a $lm1 $lm2
first_run=1
fi
-ztests for zero value. If it is the first iteration, $first_run is always empty, so continues thethenportion- within the
thenportion, we "fake" a "previous iteration" by making a duplicate of$lm1, so later ondiff, for the first iteration, is comparing two identical files, and won't report a difference. first_run=1so that next iterationif [ -z "$first_run" ]; then,$first_runwill no longer be zero-value, and so thethenportion will not be triggered, thus ensuring this action is only taken for the first iteration
Next we have the actual diff condition, that compares previous iteration's md5sum results, saved to the file referenced in the variable $lm2, compared to current iteration's md5sum results, saved to the file referenced in $lm1
if ! diff $lm2 $lm1; then
- we rely on reacting to
diffcommand's exit codes - normally
diff file1 file2results in exit code 0 when they are identical. you can test this when run$ diff a.txt a.txt; echo $?, you see a zero. When different eg$ diff a.txt b.txt; echo $?as long asb.txtis different, there will be a result1 - but
0is recognized by bash to meantrueand1meansfalse - so we cannot do
if diff $lm2 $lm1; thenbecausediffwhen files are identically, would give exit code 0, interpreted astrue, and trigger thethenpart - we want the opposite behavior, if identical do nothing, if not identical do something
!helps reverse the output
So we can test making a change,
When there is a change, the action taken is:
echo -e "$(date +"%F %R")\tChange detected:\t$1" | tee -a "$logf"
echowith-eto render\tas tabsdate +"%F %R"to render current timestamp in the given format, eg 2015-09-29 15:56|to pipe the output toteeprogramteeallows us to view the output message about there being a change, as well as save the output-asets the saving mode to append, otherwise each time there were results it would overwrite previous results
We are almost done this current iteration's tasks. Having finished the diff comparison and actions, we do this to prepare for next iteration:
mv $lm1 $lm2
mvto move/rename current iteration's md5sum result saved at$lm1, to$lm2.- so next iteration of
diff $lm2 $lm1it will indeed be comparing previous iteration's md5sum
Finally, the last line of the loop is the sleep clause sleep $interval
sleepto cause a delay, in seconds, given by$intervalvariable$intervalvariable set at the beginning of the file to be2so each iteration will last 2 seconds
done
Finally there is a
doneto close thewhile ...;do ... doneloop