views:

3275

answers:

5

eg.

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

and using the "\" escape character:

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

I'm obviously doing something stupid.

How do I get the output "BAR * BAR" ?

+2  A: 
FOO='BAR * BAR'
echo "$FOO"
ΤΖΩΤΖΙΟΥ
This works, but it's not essential to change the single quotes on the first line to double quotes.
finnw
No, it's not, but the habit to use single quotes is preferable when you're going to include special shell characters and you don't want any substitution.
ΤΖΩΤΖΙΟΥ
+2  A: 
echo "$FOO"
Rafał Dowgird
+6  A: 

Quoting when setting $FOO is not enough. You need to quote the variable reference as well:

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
finnw
A: 

It may be worth getting into the habit of using printf rather then echo on the command line.

In this example it doesn't give much benefit but it can be more useful with more complex output.

FOO="BAR * BAR"
printf %s "$FOO"
Dave Webb
Why in hell? printf is a separate process (at least, not a built-in in bash) and the use of printf you demonstrate has no benefit over echo.
ddaa
printf *is* a built-in bash and I did say in my answer "In this example it doesn't give much benefit but it can be more useful with more complex output.
Dave Webb
+7  A: 

SHORT ANSWER

Like others have said - you should always quote the variables to prevent strange behaviour. So use echo "$foo" in instead of just echo $foo.

LONG ANSWER

I do think this example warrants further explanation because there is more going on than it might seem on the face of it.

I can see where your confusion comes in because after you ran your first example you probably thought to yourself that the shell is obviously doing:

  1. Parameter expansion
  2. Filename expansion

So from your first example:

me$ FOO="BAR * BAR"
me$ echo $FOO

After parameter expansion is equivalent to:

me$ echo BAR * BAR

And after filename expansion is equivalent to:

me$ echo BAR file1 file2 file3 file4 BAR

And if you just type *echo BAR * BAR* into the command line you will see that they are equivalent.

So you probably thought to yourself "if I escape the *, I can prevent the filename expansion"

So from your second example:

me$ FOO="BAR \* BAR"
me$ echo $FOO

After parameter expansion should be equivalent to:

me$ echo BAR \* BAR

And after filename expansion should be equivalent to:

me$ echo BAR \* BAR

And if you try typing "echo BAR \* BAR" directly into the command line it will indeed print "BAR * BAR" because the filename expansion is prevented by the escape.

So why did using $foo not work?

It's because there is a third expansion that takes place - Quote Removal. From the bash manual quote removal is:

After the preceding expansions, all unquoted occurrences of the characters ‘\’, ‘'’, and ‘"’ that did not result from one of the above expansions are removed.

So what happens is when you type the command directly into the command line, the escape character is not the result of a previous expansion so BASH removes it before sending it to the echo command, but in the 2nd example, the "\*" was the result of a previous Parameter expansion, so it is NOT removed. As a result, echo receives "\*" and that's what it prints.

Note the difference between the first example - "*" is not included in the characters that will be removed by Quote Removal.

I hope this makes sense. In the end the conclusion in the same - just use quotes. I just thought I'd explain why escaping, which logically should work if only Parameter and Filename expansion are at play, didn't work.

For a full explanation of BASH expansions, refer to:

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

mithu
Great answer! Now I don't feel I asked such a dumb question. :-)
andyuk