1

In bash, I know the general rules for what single and double quotes do, but I get lost as things get nested more deeply.

For example, what is is the difference between

export VAR=$(echo "${VAL}-venv" | awk '{print tolower($0)}')

and

export VAR=$(echo "${VAL}-venv" | awk "{print tolower($0)}")

or for that matter between

$(somecmd "${ROLE_NAME}" | someothercmd)

and

"$(somecmd "${ROLE_NAME}" | someothercmd)"
Community
  • 1
  • 1
orome
  • 45,163
  • 57
  • 202
  • 418

1 Answers1

2
awk '{print tolower($0)}'

prevents the shell from expanding $0. Awk gets to see $0.

awk "{print tolower($0)}"

makes the shell expand $0 to the name of the current script or shell, something like -bash in an interactive session. Awk gets to see

print tolower(-bash)

and because it doesn't know about any variable called bash, it initializes it to 0.

$(somecmd "${ROLE_NAME}" | someothercmd)

returns the output of

somecmd "${ROLE_NAME}" | someothercmd

and the output undergoes word splitting and glob expansion;

"$(somecmd "${ROLE_NAME}" | someothercmd)"

does the same, but word splitting and glob expansion don't take place.

However, if the command substitution is on the right-hand side of an assignment, globbing and word splitting are suppressed by default:

var=$(somecmd "${ROLE_NAME}" | someothercmd)

is equivalent to

var="$(somecmd "${ROLE_NAME}" | someothercmd)"

It doesn't hurt to quote, though.

Notice that the "suppressed by default" statement only applies for the right-hand side as a whole; within it, you still have to quote. This is important when dealing with arrays: observe

$ myfunc () { echo 1 2 3; }                  # Generates three words
$ arr=($(myfunc))                            # Unquoted within ()
$ declare -p arr
declare -a arr='([0]="1" [1]="2" [2]="3")'   # Three array elements
$ arr=("$(myfunc)")                          # Quoted within ()
$ declare -p arr
declare -a arr='([0]="1 2 3")'               # One array element

And finally, "automatically quoted on the right-hand side applies only to expansions, i.e., var=$1 (no quotes needed) or var=$(command) (no quotes needed around $(); maybe needed within command itself. It does not apply when the right hand side contains spaces: var=a b c does not assign a b c to var. It tries to set an environment variable var to value a, then run command b with argument c.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • In the second example, my actual use results in different behavior. I have [an argument](http://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html) of the form `--role $(aws iam get-role --role-name "${ROLE_NAME}" | jq --raw-output '.Role.Arn')` which works fine but `--role "$(aws iam get-role --role-name "${ROLE_NAME}" | jq --raw-output '.Role.Arn')"` produces all kinds of errors. – orome Mar 25 '17 at 17:54
  • @raxacoricofallapatorius That depends on the input to `jq` and what kind of option `--role` expects – hard to tell without knowing the details. – Benjamin W. Mar 25 '17 at 17:59
  • But doesn't the processing of everything inside the outer quotes happen the same in both cases? The addition of the outer quotes should just prevent splitting and glob expansion at the very end of the whole process. And in this case the values involved are the same (no spaces of special chars). – orome Mar 25 '17 at 18:20
  • @raxaco I would have to see the exact strings to be able to tell. – Benjamin W. Mar 25 '17 at 18:27