[bash] How do I escape the wildcard/asterisk character in bash?

For example:

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?

This question is related to bash shell escaping

The answer is


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"

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


FOO='BAR * BAR'
echo "$FOO"

echo "$FOO"

I'll add a bit to this old thread.

Usually you would use

$ echo "$FOO"

However, I've had problems even with this syntax. Consider the following script.

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

The * needs to be passed verbatim to curl, but the same problems will arise. The above example won't work (it will expand to filenames in the current directory) and neither will \*. You also can't quote $curl_opts because it will be recognized as a single (invalid) option to curl.

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

Therefore I would recommend the use of the bash variable $GLOBIGNORE to prevent filename expansion altogether if applied to the global pattern, or use the set -f built-in flag.

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

Applying to your original example:

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR

If you don't want to bother with weird expansions from bash you can do this

me$ FOO="BAR \x2A BAR"   # 2A is hex code for *
me$ echo -e $FOO
BAR * BAR
me$ 

Explanation here why using -e option of echo makes life easier:

Relevant quote from man here:

SYNOPSIS
   echo [SHORT-OPTION]... [STRING]...
   echo LONG-OPTION

DESCRIPTION
   Echo the STRING(s) to standard output.

   -n     do not output the trailing newline

   -e     enable interpretation of backslash escapes

   -E     disable interpretation of backslash escapes (default)

   --help display this help and exit

   --version
          output version information and exit

   If -e is in effect, the following sequences are recognized:

   \\     backslash

   ...

   \0NNN  byte with octal value NNN (1 to 3 digits)

   \xHH   byte with hexadecimal value HH (1 to 2 digits)

For the hex code you can check man ascii page (first line in octal, second decimal, third hex):

   051   41    29    )                           151   105   69    i
   052   42    2A    *                           152   106   6A    j
   053   43    2B    +                           153   107   6B    k

Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to shell

Comparing a variable with a string python not working when redirecting from bash script Get first line of a shell command's output How to run shell script file using nodejs? Run bash command on jenkins pipeline Way to create multiline comments in Bash? How to do multiline shell script in Ansible How to check if a file exists in a shell script How to check if an environment variable exists and get its value? Curl to return http status code along with the response docker entrypoint running bash script gets "permission denied"

Examples related to escaping

Uses for the '"' entity in HTML Javascript - How to show escape characters in a string? How to print a single backslash? How to escape special characters of a string with single backslashes Saving utf-8 texts with json.dumps as UTF8, not as \u escape sequence Properly escape a double quote in CSV How to Git stash pop specific stash in 1.8.3? In Java, should I escape a single quotation mark (') in String (double quoted)? How do I escape a single quote ( ' ) in JavaScript? Which characters need to be escaped when using Bash?