-3

From bash manual

set -x

Print a trace of simple commands, for commands, case commands, select commands, and arithmetic for commands and their arguments or associated word lists after they are expanded and before they are executed. The value of the PS4 variable is expanded and the resultant value is printed before the command and its expanded arguments.

  • But I found that the traces it prints out are not exactly the results after expansion but before execution. In particular, the traces should be after quote removal, but the print out adds extra quotes or backslashes as if we could run the print out as a command in shell to get the same result as run the original command. Why is that?

  • So how can I deduce from the trace printouts the actual intermediate interpretation result after expansion but before execution?

For example, for an unset var,

$ echo '$var'   
+ echo '$var'
$var
$ echo "$var"  
+ echo ''

$ echo \$var   
+ echo '$var'
$var
$ echo \'$var\'    # why is the trace print out not: echo \'\'
+ echo ''\'''\'''
''
$ echo \'
+ echo \'
'
$ echo "'"        # why is the trace print out not: echo "'"
+ echo \'
'
$ echo "''"       # why is the trace print out not: echo "''"
+ echo ''\'''\'''
''
$ echo \'\'        # why is the trace print out not: echo \'\'
+ echo ''\'''\'''
''
$ echo \\          # why is the trace print out not: echo \\
+ echo '\'
\
2

Quoting the manual:

after they are expanded and before they are executed

You claim:

But I found that the traces it prints out are not exactly the results after expansion but before execution

But they are. The result of expansion is a list of words. Bash uses quotes to represent the list of words in an unambiguous way. What bash prints is not an intermediate result of expansion where quote removal hasn't been done. It's a printed representation of a list of strings.

For example, if a trace was

+ cp foo bar qux

then there'd be no way to know if the command was cp with three arguments foo, bar and qux, or with two arguments foo bar and qux (e.g. because the original command was cp "foo bar" qux), or with two arguments foo and bar qux, or if it was actually a command called cp foo bar qux, etc. In order to make the trace unambiguous, bash uses quotes. The quoting is chosen so that the trace is one possible way to run this command in a bash script.

For example, the trace + echo '$var' means that the command echo is executed with one argument, the 4-character string $var. There are many ways of running this command, such as:

echo '$var'
echo \$var
echo \$\v\a\r
echo "\$v"\a''''r
echo "$a"               # after running a='$var'
  • Thanks. (1) The trace printout is a modification of an intermediate result of expansion and quote removal. If it were before quote removal, then the quotes and backslashes should have be shown in the trace printouts. (2) Any particular reason that the trace of echo "''" is echo ''\'''\''' which is more redundant than echo \'\'? – Tim Aug 9 '17 at 1:34
1

It seems that the trace opts to use strong quotes when at all possible. If I had to guess, this would be for easily reproducible results independent of changes to shell environment variables, as in this case:

$ echo '$var'   
+ echo '$var'
$var

Strong quotes also seem to allow for "simpler" (i. e. non-escaped) reproductions, as in:

$ echo \\          # why is the trace print out not: echo \\
+ echo '\'
\

However, when reproducing 's, since it is opting for strong quotes, it needs to escape them. I suppose that would be simpler logic than looking at whether to use weak or strong quotes, which would potentially lead to a mess of edge-case checking.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.