3 added 178 characters in body
source | link

2019 edit. a limited subset of ksh's [[...]] is now also supported by busybox ash when built with ASH_TEST and ASH_BASH_COMPAT and a much larger subset in yash.

In any case, when writing a sh script, you should stick to the POSIX sh specification. Scripts that use [[...]] should invoke ksh, bash or zsh explicitly. bash is also a mostly POSIX sh compatible shell, but with a lot more extensions (including this one).

In any case, when writing a sh script, you should stick to the POSIX sh specification. Scripts that use [[...]] should invoke ksh, bash or zsh explicitly. bash is also a mostly POSIX sh compatible shell, but with a lot more extensions (including this one).

2019 edit. a limited subset of ksh's [[...]] is now also supported by busybox ash when built with ASH_TEST and ASH_BASH_COMPAT and a much larger subset in yash.

In any case, when writing a sh script, you should stick to the POSIX sh specification. Scripts that use [[...]] should invoke ksh, bash or zsh explicitly. bash is also a mostly POSIX sh compatible shell, but with a lot more extensions (including this one).

2 added 1081 characters in body
source | link

Other POSIX utilities that can do string comparison include expr and awk.

 awk_equal() { awk 'BEGIN{exit(!(""ARGV[1] == ""ARGV[2]))}' "$1" "$2"; }
 expr_equal() { expr "x $1" = "x $2" > /dev/null; }

 if awk_equal "$var" production; then
   echo PROD
 fi
 if expr_equal "$var" production; then
   echo PROD
 fi

Note the need to prepend "" to the values with awk to make sure we get a string comparison (otherwise 1 would be considered equal to 01 or 1e0), and x in expr for the same reason but also to avoid problems with values being expr operators.

With both awk and expr (at least POSIXly), they are not really equality operators but a test for whether the two operands have equal sorting order, which may not necessarily be the same thing. For instance, on may system, expr_equal ② ③ returns true, because neither ② nor ③ have a defined sorting order. Some awk implementations like gawk, mawk and busybox awk ignore that POSIX requirement and do a simple byte-to-byte comparison instead.

In any case, I can't think of any good reason why you'd prefer those over [ or case here.

Other POSIX utilities that can do string comparison include expr and awk.

 awk_equal() { awk 'BEGIN{exit(!(""ARGV[1] == ""ARGV[2]))}' "$1" "$2"; }
 expr_equal() { expr "x $1" = "x $2" > /dev/null; }

 if awk_equal "$var" production; then
   echo PROD
 fi
 if expr_equal "$var" production; then
   echo PROD
 fi

Note the need to prepend "" to the values with awk to make sure we get a string comparison (otherwise 1 would be considered equal to 01 or 1e0), and x in expr for the same reason but also to avoid problems with values being expr operators.

With both awk and expr (at least POSIXly), they are not really equality operators but a test for whether the two operands have equal sorting order, which may not necessarily be the same thing. For instance, on may system, expr_equal ② ③ returns true, because neither ② nor ③ have a defined sorting order. Some awk implementations like gawk, mawk and busybox awk ignore that POSIX requirement and do a simple byte-to-byte comparison instead.

In any case, I can't think of any good reason why you'd prefer those over [ or case here.

1
source | link

[[...]] is a Korn shell construct also supported by bash and zsh but otherwise not a standard sh one (and not supported by any other shell).

busybox sh is based on ash that implements a subset of the POSIX specification of sh (in the POSIX locale, it is compliant for the most part) with very few extensions, and in particular, not this one.

In any case, when writing a sh script, you should stick to the POSIX sh specification. Scripts that use [[...]] should invoke ksh, bash or zsh explicitly. bash is also a mostly POSIX sh compatible shell, but with a lot more extensions (including this one).

Testing for x being non-empty, and then being equal to production makes little sense. If x == production, then obviously it's not empty.

Do do string comparisons with the POSIX shell and utilities, you have a few options, the most obvious ones in this case are:

  • the [ utility aka test, generally built in the shell:

    if [ "$var" = production ]; then
      echo PROD
    fi
    
  • the case construct:

    case $var in
      production) echo PROD;;
      "") echo EMPTY;;
      *) echo non-PROD;;
    esac
    

Now, you may want to check that the variable is set (as opposed to non-empty), prior to dereferencing it, in the case where the nounset option has been enabled (with set -u or set -o nounset or with #! /bin/sh -u as the she-bang line), as otherwise a [ "$ENV" = production ] would cause the shell to exit if $ENV was not set. To do that, you'd do:

if [ "${var+set}" = set ] && [ "$var" = production ]; then
  echo PROD
fi

(you should avoid the -a [ AND operator as it's deprecated and unreliable).

Though a better and more canonical way to do it would be:

if [ "${var-}" = production ]; then
  echo PROD
fi

nounset does not trigger on ${var+string} or ${var-string} expansions. ${var-} expands to the content of $var if the variable is set or the empty string otherwise.

A few other notes:

$ENV is a special variable for sh. When started interactively, it's taken as the path to a file to read initialisations from (the equivalent of ~/.bashrc for bash). You should not use it for other purposes.

You'll find that some old Unix shell scripting literature recommend to use [ "x$var" = xproduction ] or [ production = "x$var" ] to do string comparison. That was to overcome bugs in some old versions of the [ and test utility that were confused by some values of $var like !, ( or -n. It should not be needed on modern systems, but something to bear in mind for very old systems. In any case, the case construct doesn't have this kind of problem.