# Bash: call make when directory changes

I usually like to use entr when developing on my own hardware to run commands when a directory changes.

However, sometimes I'm working in an environment where I don't have much control over the availability of helpful programs so many of us take for granted.

I've been using Git Bash on Windows, and I've been getting tired of hunting down my build terminal, pressing up in my last history to get to the build command, and hitting enter to build my project. I want it to automatically run when I save a new change. But I don't have entr or any of the other usual suspects (like inotifywait) to assist me.

So to overcome this problem, with a bit of Internet searching to assist, I wrote a Bash function to watch the src/ directory and build the project when anything in the directory changes (including timestamps, because -l):

monitor_and_build () {
local sauce=$(ls -l$1)
while [ true ] ;
do
sleep 1
local newsauce=$(ls -l$1)
if [[ "$sauce" != "$newsauce" ]]; then
make
fi
local sauce="$newsauce" done }  to be invoked: $ monitor_and_build src/


I have a working directory Makefile that calls make -C src/.

Since I did it in such an ad-hoc way, I'm sure I missed a trick or there's some edge cases this has a problem with. Please review!

Bonus question: aside from the obvious "writing your own programs in Bash is error-prone and foolish" (I admit it took me a few drafts to get it working) - suppose I had entr available, and then what are the benefits of using a program like entr instead?

You're going to get false positives by comparing the output of ls -l. In many locales, this command gives date stamps in different formats for newer files (day+time) and older files (year+day). When any file transitions from "new" to "old" in this scheme, you'll get a spurious notification. If you have the GNU implementation of ls, you can make it use a specific timestamp format (e.g. --time-style=full-iso) which might alleviate that problem. Or just accept the false positives (make should be reasonably quick if nothing actually changed).

The usual problem of failing to quote arguments ($1 where you should have written "$1") will mess up arguments that contain whitespace or shell pattern characters. Although that might be unlikely for your source directories, it's trivial to fix, and a good habit to have.

Testing that the string true is non-empty ([ true ]) is an unusual approach to making an infinite loop - most shell programmers would simply use the true command. Actually, it's probably better to test the result of sleep, which returns non-zero if interrupted.

Instead of polling, you should be able to block on filesystem notification events. Perhaps inotifywait meets your needs here?

If we use plain [ rather than [[, the code becomes portable (POSIX) shell, without needing Bash extensions, so is useful to more people.

Is it right that you want to run make in the current directory? For some users, it might make more sense to make -C "$1". You could be more flexible by allowing a command to be specified (here I've kept make as default): monitor_and_build() { local d=${1:-.}; shift
test -d "$d" || return 1 local sauce=$(ls -l --time-style=full-iso "$d") while sleep 1 do local newsauce=$(ls -l --time-style=full-iso "$d") if [ "$sauce" != "$newsauce" ] then "${@:-make}"
fi
local sauce="\$newsauce"
done
}

• Ah, I'd not come across entr before, so didn't realise that inotifywait was similar. Thanks for the clarification. If you have the GNU version of ls, you can specify the timestamp format to use - that's probably useful for this. Apr 21 at 14:28
• Already edited (I think it's a shame that SE doesn't notify the asker when answers get edited!) Yes, --full-time would be a good choice (and equivalent to what I suggested). Apr 21 at 14:36
• The -l also becomes redundant when specifying --full-time, (but not for --time-style). Maybe we should just have --full-time in the code. Apr 21 at 14:58