bashshellbooleanis-emptytruthiness

Bash: Safe to remove all occurrences of -n inside brackets?


I recently found this "bug" in my bash script;

if [ "$var" ]; then
    echo "Condition is true"
fi

where what I meant was to check if $var is non-empty, i.e. [ -n "$var" ]. As it happens, the code seems to work perfectly fine without the -n. Similarly, I find that I can replace [ -z "$var" ] with [ ! "$var" ].

I tend to like this implicit falseyness (truthiness) based on the (non-)emptyness of a variable in other languages, and so I might adopt this pattern in bash as well. Are there any danger to doing so, i.e. edge cases where the two set of syntaxes are inequivalent?


Solution

  • So we substitute:

    -n "$var"   ->    "$var" 
    -z "$var"   ->    ! "$var"
    

    That looks ok and some people do that. There are corner cases where the removal will be harmful and result in syntax errors. These corner cases specially include times, where var is equal to a valid test argument. Ex. var=-n or var="!" etc.

    Ex:

    $ v1="-n" v2=""; [ -n "$v1" -o -z "$v2" ]; echo $?
    0
    

    but

    $ v1="-n" v2=""; [ "$v1" -o ! "$v2" ]; echo $?
    bash: [: too many arguments
    2
    

    That said, I couldn't find a way on my system (bash 5.0.0) to break it without using -o and -a. And anyway, the test man page advises to use && and || instead of -a and -o.

    But in POSIX specification test we can find the following application note:

    The two commands:

    test "$1"
    test ! "$1"

    could not be used reliably on some historical systems. Unexpected results would occur if such a string expression were used and $1 expanded to '!', '(', or a known unary primary. Better constructs are:

    test -n "$1"
    test -z "$1"

    So I think as long as you don't use "some historical systems", you are safe. But is it worth the risk? You have to answer yourself.

    That said, subjective: I value maintainability and readability much more then saving to type 3 characters and find -n and -z more readable. The intent with -n is clear - test if the string has -nonzero length. The intent with -z is also clear - test if the string has -zero length. I find it confusing to others to write [ "$var" ] and [ ! "$var" ]. At first it would look like $var has some special meaning inside the [ ].

    I recently found this "bug" in my bash script

    It's not a bug, it's a feature!