At work, there was a shell script someone had written. It downloaded and installed the packages we needed, adjusted config files, and brought the service up with systemctl. Roughly, it looked like this:

install_blabla() {
	dest=$(mktemp /tmp/blabla_XXXX.deb)
	curl -fsSL http://blabla.com/blabla.deb -o $dest
	dpkg -i $dest
	rm -f $dest
}

install_conf() {
	curl -fsSL http://blabla.com/blabla.conf -o /etc/blabla/blabla.conf
	sed -i -e "..." /etc/blabla/blabla.conf
}

start_blabla() {
	systemctl start blabla
}

do_install() {
	install_blabla
	install_conf
	start_blabla
}

do_install

If you follow that script, the flow is:

  1. Download blabla.deb with curl
  2. Install it with dpkg
  3. Download blabla.conf with curl
  4. Edit blabla.conf with sed
  5. Start blabla with systemctl

If everything runs in that order, blabla should obviously start with the modified config. But we got a report that blabla was behaving oddly, and it looked as if it was loading the default config rather than the one we had changed.

No matter how we checked, the config file on disk really was updated correctly, and it wasn’t reading some other path either.

Then one suspicion suddenly flashed through my mind.

“What if, for some unknown reason, the download of blabla.conf hadn’t finished before the next line ran?”

So I restarted blabla, and sure enough, it came up using the edited config. Suddenly I was thoroughly confused.

“Wait. Bash isn’t Node.js. How could curl not finish before the next statement runs? Bash isn’t async!”

I googled it just in case there was something I didn’t know, found nothing, and remained baffled about what had actually happened, until a colleague sitting next to me nailed it.

“This looks like it’s starting right after dpkg installs it.”

And that, of course, was the real cause. The problem was not Bash running asynchronously. On Ubuntu, when you install a package with dpkg, if the package ships with a service, it can be enabled and started automatically as part of the install.

So in the end, I had doubted Bash for absolutely no reason.

Sorry, Bash.