Bash string comparison

768 Views Asked by At

I am trying to compare two strings in a Bash script and I am getting very strange results.

if [[ "010" < "01." ]]; then echo "Wrong"; else echo "OK"; fi
if [[ "010" < "01.0" ]]; then echo "Wrong"; else echo "OK"; fi
if [ "010" \< "01." ]; then echo "Wrong"; else echo "OK"; fi
if [ "010" \< "01.0" ]; then echo "Wrong"; else echo "OK"; fi

Reading the documentation, it seemed that [[ < ]] and [ \< ] should work the same, but they don't. It it seems that [[ < ]] works wrong when the strings don't have the same length. Am I missing something?

The expected result is 4 x OK. Tested on:

  • CentOS release 6.4 (Final) - GNU Bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu) (OK Wrong OK OK)
  • Ubuntu 14.04.2 (Trusty Tahr) LTS - GNU Bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) (OK Wrong OK OK)
  • openSUSE 13.1 (Bottle) (x86_64) - GNU Bash, version 4.2.53(1)-release (x86_64-suse-linux-gnu) (OK OK OK OK)
3

There are 3 best solutions below

5
On

Here is the documentation from help test:

STRING1 > STRING2

True if STRING1 sorts after STRING2 lexicographically.

Taking your first if statement as an example:

if [[ "010" < "01." ]]; then echo "Wrong"; else echo "OK"; fi

In Bash the string "01." sorts lexicographically before the string "010" (you can test in other tools like Microsoft Excel), so the comparison returns false. This is the case for all 4 of your comparisons.

Note that adding an additional 0 to the end of "01." doesn't change the ordering versus "010", so you still get the same result.

0
On

The reason might be that starting with Bash 4.1 the < and > string comparison operators respect the locale.

So between two systems you can have the following differences:

  1. different locales - check by running locale (for a meaning of the different locale settings, see What does "LC_ALL=C" do?)
  2. even if the locales are the same, shopt compat31 or compat32 might be switched on (Bash 3.1 or 3.2 compatibility mode), which means that the string comparisons would not respect locale - check by running shopt

Further reading:

2
On

If you want to compare integers you shall do this:

if [[ 10 < 1 ]]; then echo "10 < 1"; else echo "10 > 1"; fi

# Result:
#
#   10 > 1

If you want to compare floats you should look at this thread: How can I do float comparison in Bash?

For your example:

echo "10 < 1.0" | bc
# Return: 0

echo "10 > 1.0" | bc
# Return: 1