Watch Files for Changes With Bash
How to make a simple bash script that watches files for changes.
By. Jacob
Edited: 2019-10-07 16:32
I created a short bash (.sh) script to show how to watch for changes in files. Sometimes you might want to do something whenever a file is changed. One example is the re-compiling or minimization of CSS and JavaScript files whenever they are edited.
There are already tools that does this, but you might want to make your own, for whatever reason. In bash, this is very simple.
To test the script, simply place it inside a directory full of files, run the script, and try changing a file while the script is running. It might take a few seconds before it discovers your changes—just wait a bit.
This scripts gives you a solid base to work with. Have fun! :-D
#!/bin/sh
# Script to watch files for changes
# Declare an associative array (-A)
declare -A files_array
# You can also make functions in bash,
# this syntax should be fairly well supported
make_md5sums () {
i=0
while read file
do
files_array["$file"]=$(md5sum "$file" | cut -d ' ' -f 1)
(( i++ ))
done < <(ls)
}
check_md5sums () {
for file in "${!files_array[@]}"
do
stored_md5sum=${files_array["$file"]}
current_md5sum=$(md5sum "$file" | cut -d ' ' -f 1)
if [ $stored_md5sum != $current_md5sum ]
then
echo "$file has been changed."
exit
fi
done
sleep 1s # Wait a second before checking again
}
# ------
# This is where we run our functions
# ------
make_md5sums
while true
do
check_md5sums
done
Explanation
The first line #!/bin/sh tells the system that the script should be run with sh. But, depending on your system, this could be a symbolic link to either bash or dash. There are differences, which is also why scripts you find online might not work on all systems.
Next, we declare an associative array (the files_array). We will use this to store the md5 sums of all the files in the directory, along with the file name. The "-A" part means we are declaring an associative array.
#!/bin/sh
# Script to watch files for changes
# Declare an associative array (-A)
declare -A files_array
The next part of the script is the make_md5sums() check_md5sums() functions. Functions are useful because they increase code re-usability. Hopefully we are all following the DRY principle ;-)
The make_md5sums() function will generate md5 hashes from the content of all files in the directory, and store each md5 sum in the files_array, using the file name as key. This makes it easy to fetch the md5 sum for each file, simply by doing something like: echo ${files_array["some_file_name.txt"]}.
Pay extra attention to the syntax around storing the md5sum hash, because it is very nasty compared to other scripting languages. I had to use "|" and "cut" to get rid of the file name, which is included in the md5sum output.
make_md5sums () {
i=0
while read file
do
files_array["$file"]=$(md5sum "$file" | cut -d ' ' -f 1)
(( i++ ))
done < <(ls)
}
check_md5sums () {
for file in "${!files_array[@]}"
do
stored_md5sum=${files_array["$file"]}
current_md5sum=$(md5sum "$file" | cut -d ' ' -f 1)
if [ $stored_md5sum != $current_md5sum ]
then
echo "$file has been changed."
exit
else
sleep 1s # Wait a second before checking again
fi
done
}
The last part is a simple never ending loop. It will continually call the check_md5sums() function until it finds a change, in which case the exit inside of the check_md5sums() is called.
# ------
# This is where we run our functions
# ------
make_md5sums
while true
do
check_md5sums
done
Tell us what you think: