We are unknowingly doing a mistake in writing our linux shell scripts, and most often we are not paying attention to the syntax according to it’s interpreter. Consider the shell script below:
#!/bin/sh service=myservice if [ "$1" == "install" ] then echo "Installing $service to boot" cp $service /etc/init.d update-rc.d $service defaults elif [ "$1" == "remove" ] then echo "Removing $service from boot" update-rc.d $service disable update-rc.d -f $service remove rm -f /etc/init.d/$service else echo "Usage: ./install.sh [install|remove]" fi
It is a very naïve and simple script to install a
myservice script at boot (or to remove it). For a casual personal thing, this looks all good right? Great, let’s execute this as
./test.sh and you get:
./test.sh: 3: [: unexpected operator ./test.sh: 8: [: unexpected operator Usage: ./install.sh [install|remove]
The reason for this error is that the interpreter
sh is looking for the operator of comparison
'=' but the operator we all get taught for comparison is
'=='. The use of the interpreter
sh was decided at the topmost line called the shebang line.
Now we have two solutions in front of us. Either fix the syntax according to
sh or use a more modern and elaborated
bash which works according to the syntax you are more familiar with. A quick google search tells you that “just use bash” is the popular answer and the straight answer is lingering down somewhere on the mighty stackoverflow.
I am making a case that we should try to stick to
sh as much as possible and cave in to use
bash only when the requirement has complexity which asks for it. Let’s take a look.
sh is Omnipresent
sh offers better portability across all linux distributions and that is because they all are required to have
sh. In other words,
sh is omnipresent! If you have ever broken your system while fiddling with it, you must have been thrown in this cold dark place with a monochrome shell. Yup, that’s
When your system enters emergency mode, it has loaded nothing, has mounted nothing, started no services. But, at-least in that broken state it has got
sh for you so that you can fix whatever shenanigans you have done in boot scripts. How about that?
So is there some kind of rule or standard or norm, that barring other things,
sh has to be there on a linux system? Well it is what is called a “POSIX Standard”
sh is Standard
The syntax of
sh is very minimal. This allows for a professional scripting person to remember the entirety of it more easily. A glaring example is that it doesn’t support arrays. This is because
sh is standardized and pans even outside to unix. In fact, that is where
sh has come from. It is also known as Bourne shell named after the programmer who made it (and thankfully didn’t call it
bs). The knowledge that a certain thing is a standard definitely gives a bit of comfort.
Being the minimal program in itself, it is also low on resources. I would make this a bigger point but newer better shells have come along (e.g.
dash) which are lower on resources. And in fact, some systems simply link
dash or to
bash. Which takes us to the next point …
sh Syntax is a Subset
For various reasons, some linux distributions do not package
sh at all and simply link
bash and call it a day. In other words, there is no
sh. You may have been using
sh on that system, but it was
bash all along.
I found this immediately funny until immediately I didn’t. Any shell will be a superset of
sh which means whatever shell your system is replacing
sh with, has the obligation of supporting
sh syntax. This makes a very good compatibility point that you have maximum of it, backwards or forwards, if your scripts are written in
So that was my case for adhering to
sh syntax with the
#!/bin/sh shebang line up top when you are writing any shell scripts on a linux system. Yes, at times your requirement will be complex enough that
bash with its fancy arrays, simpler conditional syntax and many more things, will seem like the best choice. No denying that. And if things are going complex enough, then you should assess if an outsider
python is the right candidate, because that really opens up the possibilities of handling complexities.
But for all practical purposes and everyday shell scripting, bourne shell’s syntax should be the way to go. If your complexity increases mid way, you can always switch it up to
bash and whatever you have already written will continue to work by design.