86

I see this often in the build scripts of projects that use autotools (autoconf, automake). When somebody wants to check the value of a shell variable, they frequently use this idiom:

if test "x$SHELL_VAR" = "xyes"; then
...

What is the advantage to this over simply checking the value like this:

if test $SHELL_VAR = "yes"; then
...

I figure there must be some reason that I see this so often, but I can't figure out what it is.

jonner
  • 6,331
  • 6
  • 30
  • 28
  • 2
    See also [Shell script purpose of x in `"x$Variable"`](http://stackoverflow.com/questions/1805663/shell-script-purpose-of-x-in-xvariable/). That question is a duplicate of this one. – Jonathan Leffler Dec 29 '13 at 16:37
  • 2
    See also [bash test for empty string with `X""`](http://stackoverflow.com/questions/6852612/bash-test-for-empty-string-with-x). That question too is effectively a duplicate of this one, or covers most of the same ground. Both the duplicates have some good answers. – Jonathan Leffler Dec 29 '13 at 16:45

7 Answers7

107

If you're using a shell that does simple substitution and the SHELL_VAR variable does not exist (or is blank), then you need to watch out for the edge cases. The following translations will happen:

if test $SHELL_VAR = yes; then        -->  if test = yes; then
if test x$SHELL_VAR = xyes; then      -->  if test x = xyes; then

The first of these will generate an error since the fist argument to test has gone missing. The second does not have that problem.

Your case translates as follows:

if test "x$SHELL_VAR" = "xyes"; then  -->  if test "x" = "xyes"; then

The x, at least for POSIX-compliant shells, is actually redundant since the quotes ensue that both an empty argument and one containing spaces are interpreted as a single object.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 3
    Wouldn't quoting solve the empty var edge case `test "$SHELL_VAR" = yes` more elegantly than `xyes`? For option processing, would changing the order `'abc' = "$1"` be another option? – Ciro Santilli OurBigBook.com Jun 29 '16 at 03:38
  • Why would the first argument to `test` go missing and not become an empty string? Are different shells handling this differently? – phk Jun 29 '16 at 19:41
  • 1
    @phk: try `export xxx=''`, `if test $xxx = 1 ; then echo 2 ; fi`, `export xxx='yyy'`, `if test $xxx = 1 ; then echo 2 ; fi`. The first `if` gives an error, the second does not. This is in `bash`. – paxdiablo Jun 30 '16 at 03:00
  • My mistake, I was testing using ’printf’ which apparently always assumes there is a second parameter. – phk Jun 30 '16 at 08:48
  • But which shells perform "a simple substitution?" I tested BASH, DASH and KSH. All of them treat an empty set of quotes `""` as an empty parameter. Therefore `test "$VAR" = foo` is safe in them even if `VAR` is empty. – SnakE Nov 13 '20 at 11:07
  • 1
    @SnakE: the POSIX-compatible shells do that safely, I stated that in my closing paragraph. Jonathan Leffler's answer explains why the `x` may have been used in older shells. – paxdiablo Nov 13 '20 at 12:01
25

The other reason that no-one else has yet mentioned is in relation to option processing. If you write:

if [ "$1" = "abc" ]; then ...

and $1 has the value '-n', the syntax of the test command is ambiguous; it is not clear what you were testing. The 'x' at the front prevents a leading dash from causing trouble.

You have to be looking at really ancient shells to find one where the test command does not have support for -n or -z; the Version 7 (1978) test command included them. It isn't quite irrelevant - some Version 6 UNIX stuff escaped into BSD, but these days, you'd be extremely hard pressed to find anything that ancient in current use.

Not using double quotes around values is dangerous, as a number of other people pointed out. Indeed, if there's a chance that file names might contain spaces (MacOS X and Windows both encourage that to some extent, and Unix has always supported it, though tools like xargs make it harder), then you should enclose file names in double quotes every time you use them too. Unless you are in charge of the value (e.g. during option handling, and you set the variable to 'no' at startup and 'yes' when a flag is included in the command line) then it is not safe to use unquoted forms of variables until you've proved them safe -- and you may as well do it all the time for many purposes. Or document that your scripts will fail horribly if users attempt to process files with blanks in the names. (And there are other characters to worry about too -- backticks could be rather nasty too, for instance.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 4
    I would say there is no ambiguity when test core is properly quoted as in your first example. In this case, there are 3 arguments even if some values are empty, such as [ "-n" = "" ]. So `test` implementation should not enter the [ -n "$var" ] branch case (which requires 2 arguments only). Personally, each time I had problems, there were either related to missing quotes around $var resulting to error "=: unary operator expected", or -z option not existing, or complex use of `test` inside its core [ ... ] instead of using multiple [ test1 ] || [ test2 ] at shell level (as @Jay mentioned). – user1556814 Oct 28 '15 at 07:09
  • 3
    If you can rely on modern implementations, maybe you can get away with, though it is hard to read your comments at times. Historically, some implementations of `test` did bizarre things, and the technique protected against those bizarre implementations. You may be lucky enough not to have to worry about it these days. – Jonathan Leffler Oct 28 '15 at 07:15
  • As far as: *but these days, you'd be extremely hard pressed to find anything that ancient in current use*: it's used in macOS even now. Example in */usr/libexec/locate.mklocatedb*: *if [ X"$1" = "X-presort" ]; then* though maybe that's not quite it .. I came here because I could not for the life of me remember why it was used though I knew in the past. – Pryftan Feb 21 '23 at 12:41
14

There's two reasons that I know of for this convention:

http://tldp.org/LDP/abs/html/comparison-ops.html

In a compound test, even quoting the string variable might not suffice. [ -n "$string" -o "$a" = "$b" ] may cause an error with some versions of Bash if $string is empty. The safe way is to append an extra character to possibly empty variables, [ "x$string" != x -o "x$a" = "x$b" ] (the "x's" cancel out).

Second, in other shells than Bash, especially older ones, the test conditions like '-z' to test for an empty variable did not exist, so while this:

if [ -z "$SOME_VAR" ]; then
  echo "this variable is not defined"
fi

will work fine in BASH, if you're aiming for portability across various UNIX environments where you can't be sure that the default shell will be Bash and whether it supports the -z test condition, it's safer to use the form if [ "x$SOME_VAR" = "x" ] since that will always have the intended effect. Essentially this is an old shell scripting trick for finding an empty variable, and it's still used today for backwards compatibility despite there being cleaner methods available.

Jay
  • 41,768
  • 14
  • 66
  • 83
5

I recommend instead:

if test "yes" = "$SHELL_VAR"; then

since it does away with the ugly x, and still solves the problem mentioned by https://stackoverflow.com/a/174288/895245 that $SHELL_VAR may start with - and be read as an option.

Community
  • 1
  • 1
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
2

I believe its due to

SHELLVAR=$(true)
if test $SHELLVAR  = "yes" ; then echo "yep" ; fi 

# bash: test: =: unary operator expected

as well as

if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi
# bash: test: =: unary operator expected

and

SHELLVAR=" hello" 
if test $SHELLVAR = "hello" ; then echo "yep" ; fi
# yep 

however, this should usually work

SHELLVAR=" hello"
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi 
#<no output>

but when it complains in output somewhere else, its hard to tell what its complaining about I guess, so

SHELLVAR=" hello"
if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi 

works just as well, but would be easier to debug.

Kent Fredric
  • 56,416
  • 14
  • 107
  • 150
-1

I used to do that in DOS when the SHELL_VAR might be undefined.

Ken
  • 2,092
  • 1
  • 19
  • 16
-1

If you don't do the "x$SHELL_VAR" thing, then if $SHELL_VAR is undefined, you get an error about "=" not being a monadic operator or something like that.

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
  • This is only true if you aren't quoting correctly; and if you don't quote correctly, your shell scripts have bigger problems that using `x$FOO` won't fix. – Charles Duffy Aug 25 '19 at 23:05