When instructed to echo commands as they are executed ("execution trace"), both bash
and ksh
add single quotes around any word with meta-characters (*
, ?
, ;
, etc.) in it.
The meta-characters could have gotten into the word in a variety of ways. The word (or part of it) could have been quoted with single or double quotes, the characters could have been escaped with a \
, or they remained as the result of a failed filename matching attempt. In all cases, the execution trace will contain single-quoted words, for example:
$ set -x
$ echo foo\;bar
+ echo 'foo;bar'
This is just an artifact of the way the shells implement the execution trace; it doesn't alter the way the arguments are ultimately passed to the command. The quotes are added, printed, and discarded. Here is the relevant part of the bash
source code, print_cmd.c
:
/* A function to print the words of a simple command when set -x is on. */
void
xtrace_print_word_list (list, xtflags)
...
{
...
for (w = list; w; w = w->next)
{
t = w->word->word;
...
else if (sh_contains_shell_metas (t))
{
x = sh_single_quote (t);
fprintf (xtrace_fp, "%s%s", x, w->next ? " " : "");
free (x);
}
As to why the authors chose to do this, the code there doesn't say. But here's some similar code in variables.c
, and it comes with a comment:
/* Print the value cell of VAR, a shell variable. Do not print
the name, nor leading/trailing newline. If QUOTE is non-zero,
and the value contains shell metacharacters, quote the value
in such a way that it can be read back in. */
void
print_var_value (var, quote)
...
{
...
else if (quote && sh_contains_shell_metas (value_cell (var)))
{
t = sh_single_quote (value_cell (var));
printf ("%s", t);
free (t);
}
So possibly it's done so that it's easier to copy the command lines from the output of the execution trace and run them again.
In your script, these assignments
normal='\e[0m'
yellow='\e[33m'
put those characters literally into the variables, i.e., \e[0m, rather than the escape sequence. You can construct an escape character using printf
(or some versions of echo
), e.g.,
normal=$(printf '\033[0m')
yellow=$(printf '\033[33m')
but you would do much better to use tput
, as this will work for any correctly set up terminal:
normal=$(tput sgr0)
yellow=$(tput setaf 3)
Looking at your example, it seems that the version of printf
you are using treats \e
as the escape character (which may work on your system, but is not generally portable to other systems). To see this, try
yellow='\e[33m'
printf 'Yellow:%s\n' $yellow
and you would see the literal characters:
Yellow:\e[33m
rather than the escape sequence. Putting those in the printf
format tells printf
to interpret them (if it can).
Further reading:
Best Answer
What the manual says is that the shell will avoid any loop that may be caused by recursion of alias expansion.
With your example (
a1=a2=a3=a4
), if you executealias a4=a1
you are creating a loop. Then, as soon as you will executea1
(resp.a2
,a3
,a4
), once the shell loops back toa1
(resp.a2
,a3
,a4
) it will search for a command nameda1
(resp.a2
,a3
,a4
) that is NOT an alias (since that would create a never-ending loop).Example: