5

In the old times of DOS batch scripts, the only way to check for a variable to be empty or to allow an empty variable was to prepend (or append) a known letter, commonly the letter x, because there were no quotes:

if x%var = x  
if x%var = xab 

so I understood that programmers migrating from DOS kept the old habit, while in the bourne shell quoting has always been available (and good habit anyhow):

if test "$var" = ""
if test -n "$var"
if test "$var" = "ab" 

It becomes really odd if additonally quoted, which is fortunately quite seldom:

if [ "x$var" = "x" ] 
if [ "x$var" = "xabc" ] 

Personally, I find scipts more easy to read where operands are quoted than removing a leading x if the other operand has one too.

2

2 Answers 2

3

Even if empty vars and quotes work, some pre-POSIX implementations of test/[ have issues with values that look like operators, like !.

E.g. with heirloom-sh:

Empty vars are fine (when quoted, of course):

$ var=
$ [ "$var" = "" ] && echo yes
yes

But a ! breaks it:

$ var="!"
$ [ "$var" = "foo" ] && echo yes
test: argument expected

Adding the x to the front prevents that:

$ var="!"
$ [ "x$var" = "xfoo" ] && echo yes || echo no
no

The POSIX specification says that the implementation must check the number of arguments (apart from the final ]), and use that to determine which arguments can be operators and which ones are values. With three arguments, they must always be value-operator-value, so a ! would not be special in the first or last position. (In the middle it would be an invalid binary operator.)

You can still get a similar issue with longer expressions involving -a or -o (which is pretty much why they shouldn't be used). E.g. with Bash 5.2:

$ [ 'x' = x -a 1 = 1 ] && echo yes
yes
$ [ '!' = x -a 1 = 1 ] && echo yes
bash: [: too many arguments

See the answer by Stéphane Chazelas on the duplicate question for more background and corner cases.

1

Because, if $var is unset, or set empty (var="") the expression

$var = "abc"

expands to

     = "abc"

which the shell's parser does not like, while

x$var = "xabc"

expands to

x = "xabc"

which is a valid expression.

Before shells had better ways of handling unset variables, this is what we did.

Plus, "if it works, don't fix it". The amount of effort devoted to finding and updating (and testing) (and figuring out how to test) all extant copies of distributed shell scripts using this construction is 100% Monkey Motion, 100% wasted time. Don't fix old syntactic offenses against Good Taste, spend your time making new mistakes.

4
  • 4
    The question is why not [ "$var" = "thing" ] That does work when $var is unset, as long as the quotes are there? Is there a shell that doesn't do that? Commented Oct 20 at 23:22
  • 1
    @davolfman: as in the answers: old shells: with simple rules "" expand to nothing, and the additional parsing step will be equivalent to put nothing. Now we handle better such case, but with much complex shell parsing rules and [ (and time) as built-in else rules we cannot handle expected behaviour. -- In past we had much more rudimental shells (and variety). Commented Oct 21 at 6:32
  • 1
    @GiacomoCatenazzi, perhaps, but which shell would that be? Commented Oct 21 at 7:21
  • No, it never was about empty/blank/unset $var expanding to no argument. See duplicate Q&As. That problem only occurs when you forget to quote $var, but then all sorts of values cause problems (including security vulnerabilities in shells like bash or ksh) with or without the x prefix. Commented Oct 21 at 7:59

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.