2

The following script:

$ cat runme01.sh 
#!/bin/bash

A=myval
B=$A/{fix}
C=$A/fix

set -xT

echo $B
echo $C

prints the following if ran:

$ ./runme01.sh 
+ echo 'myval/{fix}'
myval/{fix}
+ echo myval/fix
myval/fix

As you see, variable B is passed to echo in single quotes, while variable C is passed w/o them.

Probably curly braces cause this.

I would like to pass them w/o quotes always. How to do that?

  • 3
    you're not doing anything at all; set -x is apparently adding the quotes, to prevent unintentional brace expansion. What is the actual problem you're trying to solve (by passing variables around)? (Also, always quote your variables!) – Jeff Schaller Jun 14 at 16:25
  • 3
    That's just how set -x shows the commands. No quotes are involved (but double quotes should've been involved on your side). – choroba Jun 14 at 16:27
6

Never try to interpret the trace output as anything other than something purely informational. It's debugging output.

The fact that it shows the braces in single quotes does not mean very much other than that the shell interpreted that particular string as myval/{fix} and nothing else. In particular, it does not mean that the string was passed quoted! The shell adds the single quotes in the trace output because braces are sometimes special, however, in this case they are not (because you never use an actual brace expansion).

The same happens here (running in an empty directory):

$ echo *
+ echo '*'
*

There is no quoting of the * in my command, so the shell will try to expand it. It does not expand to anything, so it remains unexpanded. The code that produces the tracing output notices that there is a * in a string and quotes it. It's a mechanical adding of single quotes based on what character are present in the string that is printed by the tracing, it does not affect the command or its outcome in any way whatsoever.

To illustrate this again:

$ ls -l
total 0
-rw-r--r--  1 kk  wheel  0 Jun 14 20:15 "filename"
-rw-r--r--  1 kk  wheel  0 Jun 14 20:13 'filename'
-rw-r--r--  1 kk  wheel  0 Jun 14 20:14 *
-rw-r--r--  1 kk  wheel  0 Jun 14 20:14 **
-rw-r--r--  1 kk  wheel  0 Jun 14 20:14 123
-rw-r--r--  1 kk  wheel  0 Jun 14 20:14 file
-rw-r--r--  1 kk  wheel  0 Jun 14 20:14 long name
-rw-r--r--  1 kk  wheel  0 Jun 14 20:14 {}
$ echo *
+ echo '"filename"' ''\''filename'\''' '*' '**' 123 file 'long name' '{}'
"filename" 'filename' * ** 123 file long name {}

I.e., certain characters in a string will cause the code that produces the tracing output to add quotes around that string in bash. The shell also escapes single quotes in strings.

Compare this with dash or pdksh:

$ echo *
+ echo "filename" 'filename' * ** 123 file long name {}
"filename" 'filename' * ** 123 file long name {}

These particular shells do not add the quotes (or escape single quotes). It really doesn't matter, it's just debugging output.


Always double quote variable expansions, unless you explicitly want the shell to perform word-splitting and filename generation (globbing) on the strings.

Double quoting is not always needed, but it's easier to remember to always double quote than to memorize the contexts in which you don't need to.

Your script, corrected:

#!/bin/bash

A=myval
B="$A/{fix}"
C="$A/fix"

set -xT

printf '%s\n' "$B"
printf '%s\n' "$C"

This will ensure that you will always get the values $B and $C printed to standard output, regardless of their values. In your original code, if $A had been *, using $B and $C unquoted may have pulled in some matching filenames.

Note that the trace output will still do that quoting of the myval/{fix} string with this code. Don't worry, it doesn't mean anything, it's just the shell trying to be helpful with its debugging output.

Related:

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.