Quotes, variable expansion, wildcards, and everything else mentioned in the bash manual section on expansion is handled by bash.
For example, when you run the bash command echo "x is: $x"
, bash parses this to find out that it needs to run the command echo
with one argument which is x is: 12345
. Given echo "x is" "$x"
, bash runs echo
with two arguments: x is
and 12345
— this is why the two spaces inside the quotes are preserved (they're part of the argument which echo prints unmodified), but the two spaces outside the quotes are not (for the shell, two spaces to separate arguments are as good as a single one, and echo
always prints a single space in between).
The echo
command (or any other command) has no way to know which shell command produced the argument x is: 12345
, or indeed whether a shell was involved at all. Here are a few sample commands that produce this argument:
echo "x is: 12345"
echo 'x is: 12345'
echo x\ is:\ 12345
echo "x is: $x"
echo 'x is:'\ "$x"
echo "$(echo "x ")is: $x"
# Assume there is no other file whose name begins with x in the current directory
touch "x is: $x"; echo x*
Or it could be exec "echo", "x is: 12345"
in Perl, or execlp("echo", "echo", "x is: 12345")
in C, etc.
This holds for every command. And the same principle holds for other shells, although they each have a slightly (or significantly) different set of expansions.
On the other hand, options are handled by the command. For example, ls -l -t
and ls -l "-t"
in bash (or any similar shell) both run ls
in exactly the same way, with the two arguments -l
and -t
. This is why no form of shell quoting won't help you if you want to run ls
to display information about a file called -t
. You can do that with ls -l -- -t
, i.e. by passing --
as an argument. This is a convention followed by most commands: nothing after --
is parsed as an option. Once again, this is a feature of the command; for the shell, a leading dash is nothing special.
One thing that can get tricky is backslashes. In bash and other shells, backslash outside of any quotes means that the next character loses its special meaning. For example, echo \"x\ is:\ 12345\"
prints "x is: 12345"
, because each of the characters that are preceded by a backslash lose their special meaning (quote syntax for "
, word separator for spaces). But some commands also interpret backslashes. When they do, you need to ensure that the backslash gets to them. For example, in bash, the echo
command prints backslashes literally by default, but if you pass the option -e
or run shopt xpg_echo
first, then echo
has its own escape sequences (this is for bash, the echo
command in different shells and on different systems have different rules about whether they treat backslashes specially). For example, echo 'a\nb'
prints a\nb
while echo -e 'a\nb'
prints a
-newline-b
because \n
means “newline” to echo -e
. On the other hand, echo a\nb
prints anb
since \n
is expanded by the shell and there \
means “quote the next character”.
Also, be careful when multiple shells are involved, e.g. with SSH. When you run ssh
in a shell, the local shell parses the command as usual. The SSH client joins its non-option arguments with spaces in between, and sends the resulting string to the SSH server. The SSH server passes this string to the remote shell. This means that there are two full rounds of shell expansion between what you type in a terminal or in a shell script, and what gets executed on the remote machine.
printf
instead ofecho
– George Vasiliou Dec 24 '17 at 15:46strace
it and see whatecho
is beingexecve
d as – thrig Dec 24 '17 at 16:40