Categories
Shell Scripting

Advanced Conditionals in BASH

Conditionals are one of the most misunderstood parts of bash scripting. In a programing language you use an `if` statement. This is normally one of the internal functions of the language and it starts with the statement and moves to the expression. Begin them like this:

C, PHP : if (expression)

Go, python, ruby: if expression

These all work the way you would expect and do (mostly) the same thing in each language. In fact within the command line the awk statement works the same way you would use it in C.

Bash however is different. It does NOT work the same way. In fact you can write the entire script without having to use an if statement and it *might* even be faster to do it that way. Here is how most people set up a conditional in bash:

if [ a -lt b ] ; then c ; fi
# Or

if [[ a < b ]] ; then c ; fi

But both of these don’t work in the same way as most people think. The reason why is something unique to bash because it’s a shell scripting language and not a programing language. In shell scripting you call shell commands and execute that command. `if` is a command. Type `which if` and you will see it. When you use it, it returns a “1” if the statement was true or a “0” if the statement was false. But here is where things get weird. Try this: `which [` — That’s right, ‘[‘ is also a command and it does the same thing as `if`. Now think about that… if that’s true, then this statement in bash:

if [ a -lt b ]

is similar to this in a programing language.

if ( if ( a < b ))

Do you see the difference? You have two conditionals there when you think there is only one. I personally get around this problem by condensing it. So instead of `if [ a -lt b ] ; then c ; fi` I would write the same statement like this: `[ a -lt b ] && c` As you can see, not only is it faster to run, but it’s shorter and quicker to write. I have tested these in multiple ways and on average my conditionals are 8-10% faster as a result of this technique. Here is a simple script you can use to test it yourself:

#!/bin/bash


x=0
while [ $x -lt 1000000 ] ; do
 if [ 1 -lt 2 ] ; then true ; fi
 x=$((x+1))
done
exit
#[ 1 -lt 2 ] || true

I named this script test and used the command `time ./test` in order to find out which is faster. To test them, switch out the if line and the commented line (uncomment it, comment the if and switch the location of the two lines). I moved the line to after the `exit` rather than just commenting it as I received more consistent results (I may write more on why later, to put it briefly (and poorly), bash still reads comments but ignores them)

I will make one caveat to this. My method uses about 20% more system resources rather than user resources even through the overall resource usage is lower. So system resources go up and user resources go way down. In most cases this doesn’t matter at all however there are a few very specific cases where it will (and in those cases I DO NOT suggest using bash as your go-to programing language, try C or Go or if you have to use a shell then csh)

If you test this and have some results, I would love to see them!

Leave a Reply

Your email address will not be published. Required fields are marked *