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,-
andn
, not zero characters),[ -n ]
evaluates to true.
...and again here:
You need to quote your variables. Without quotes you are writing
test -n
instead oftest -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.