The test Command

Moving on to line 3 of the code:

[ -n "$RBENV_DEBUG" ] && set -x

Tests and Conditions

The bracket syntax [ ... ], when combined with && or ||, is one way to write conditional logic in bash. The syntax is actually a synonym for the test command. If we run man [ or man test in the terminal, we see the following:

TEST(1)              General Commands Manual             TEST(1)

  NAME
        test, [ – condition evaluation utility

  SYNOPSIS
        test expression
        [ expression ]

  DESCRIPTION
        The test utility evaluates the expression and, if it 
        evaluates to true, returns a zero (true) exit status; 
        otherwise it returns 1 (false).  If there is no 
        expression, test also returns 1 (false).

Let's run an experiment to see how this syntax works.

Experiment- the [ ... ] and test commands

I create a new file named ./foo containing the following:

#!/usr/bin/env bash

[ 5 == 5 ] && echo "[ 5 == 5 ]";

The && in between the test condition and the echo string means that, if our test is true, we will continue executing the echo command. If our bracket syntax condition returns false, the execution will stop before the echo command, and nothing will be printed to the screen. The equivalent in Ruby would be 5 == 5 and puts '5 equals 5'.

I run chmod +x foo so I can execute the script, then ./foo:

$ ./foo       
[ 5 == 5 ]
$ 

I then add a new test with the condition to 5 == 6 to make sure the else clause also works:

#!/usr/bin/env bash

[ 5 == 5 ] && echo "[ 5 == 5 ]";

[ 5 == 6 ] && echo "[ 5 == 6 ]";

When I run it, I see:

$ ./foo       
[ 5 == 5 ]
$ 

The string [5 == 6 ] does not appear, indicating that this line's condition is falsy, and the code after && is ignored. This is what we'd expect.

I then update the script to use the test command instead of [ ... ], and repeat the experiment:

#!/usr/bin/env bash

[ 5 == 5 ] && echo "[ 5 == 5 ]";

[ 5 == 6 ] && echo "[ 5 == 6 ]";

test 5 == 5 && echo "test 5 == 5";

test 5 == 6 && echo "test 5 == 6";

Same results:

$ ./foo   
[ 5 == 5 ]
test 5 == 5
$ 

So as expected, we saw that test and [ ... ] produce the same results.

Now what about the -n flag?

Passing flags to [

When looking for docs on a certain flag for a command, we can usually just read the man entry for the command itself, and search for the flag. For a refresher on how to search a man entry for a flag, check out my blog post on the man command.

When I run man test and search for -n in the man page for test, I find the following:

-n string     True if the length of string is nonzero.

So [ -n "$RBENV_DEBUG" ] is truthy if the length of $RBENV_DEBUG is greater than zero (i.e. if the string is not empty).

Let's see if -n behaves the way we expect.

Experiment- the -n flag

First I run the following directly in my terminal:

$ FOO='foo'
$ [ -n "$FOO" ] && echo "Hi"
Hi
$ [ -n "$BAR" ] && echo "Hi"
$ [ -n "" ] && echo "Hi" 
$ 

So using the -n flag to test the length of $FOO resulted in printing "Hi" to the screen because $FOO has a greater-than-zero string length. But $BAR has a length of zero because it has not yet been set, and "" has a length of zero because it's the empty string. So nothing is printed in these two cases.

This all works as expected. Then, out of curiosity, I removed the double-quotes from $BAR:

$ [ -n $BAR ] && echo "Hi" 
Hi
$ 

Removing the quotes caused "Hi" to be printed. This was unexpected. Since $BAR hadn't been set, I expected nothing to be printed to the screen.

Lastly, I removed $BAR entirely:

$ [ -n ] && echo "Hi"     
Hi
$ 

Since I don't pass any value at all to the flag, I would expect the length of the non-existent "string" to be zero.

Why are the last two cases not returning the results I expect?

In this case, StackOverflow comes through with an answer here:

[ -n ] does not use the -n test.

The -n in [ -n ] is not a test at all. When there is only one argument between [ and ], that argument is a string that is tested to see if it is empty. Even when that string has a leading -, it is still interpreted as an operand, not a test. Since the string -n is not empty (it contains two characters, - and n, not zero characters), [ -n ] evaluates to true.

...and again here:

You need to quote your variables. Without quotes you are writing test -n instead of test -n "expression". The test command has no idea that you provided a variable that expanded to nothing.

So when double-quotes are absent, the script thinks the test is equal to [ -n ], which the interpreter reads as an operand of length 2, which is why it returns true. This is true whether I'm running [ -n ] or [ -n $BAR ].

Wrapping Up

So that's the first half of line 3 of the shim file (the part before the &&). If the $RBENV_DEBUG environment variable contains a value (i.e. if it's non-empty), then our condition is true.

But what happens if our condition is true? We'll look at that next.