Stephane Gosselin
Stephane Gosselin

Reputation: 9148

How to parse quotes in command that contains variables

Consider the following in a simple shell script meant to dump a mysql dbtable:

if [ $account -eq 1 ]; then
  echo "Dumping user table ... " 
  mysqldump --host='dbhost.com' --user=someUser -p'somePW' dbName  --no-create-info --single-transaction --tables user --where="id=${USER_ID}" > account_data/user.sql
fi

How would one have the above command properly parsed / quoted?

Running in -x mode shows the following:

mysqldump --host=dbhost.com --user=someUser '-psomePW' dbname --no-create-info --single-transaction --tables user --where=id=283

It still works, but it seems it does not look right having the arguments non-quoted, and the single quotes on password argument enclose the whole argument?!?

Why, Bash, why?!?

Upvotes: 1

Views: 58

Answers (1)

Etan Reisner
Etan Reisner

Reputation: 80931

Double and single quotes aren't magical. They simply protect the shell from interpreting the contents in ways it otherwise would.

Single quotes prevent the shell from interpreting anything inside them. The only thing that is special inside a single quoted string is a single quote and that ends the string.

Double quotes prevent filename globbing, etc. but allow variable expansions and the like.

Both quotes prevent the shell from word splitting (breaking input up into words on whitespace).

This line

echo foo bar baz

is four words to the shell.

Both of these

echo "foo bar baz"
echo 'foo bar baz'

are two "words".

After quote removal any words that are touching are combined by the shell so this is also two "words"

echo "foo bar"' baz'

Let us look at your examples now

mysqldump --host='dbhost.com' --user=someUser -p'somePW' dbName  --no-create-info --single-transaction --tables user --where="id=${USER_ID}" > account_data/user.sql
mysqldump --host=dbhost.com --user=someUser '-psomePW' dbname --no-create-info --single-transaction --tables user --where=id=283

Assume USER_ID has a value of 283.

Expand the variables and remove the quotes from both and we get

mysqldump --host=dbhost.com --user=someUser -psomePW dbName  --no-create-info --single-transaction --tables user --where=id=${USER_ID} > account_data/user.sql
mysqldump --host=dbhost.com --user=someUser -psomePW dbname --no-create-info --single-transaction --tables user --where=id=283

and you see why the quoting in both works the same way. They end up with the same result.

The quoting is all before the command execution. The command doesn't see the quotes any more than it sees the variables themselves.

If you run set -x in your shell the shell will show you the commands that are going to be run as it runs them (in an format that shows the "words" it sees).

How you choose to quote arguments like --host='dbhost.com' is up to personal style and what the value is. Those quotes aren't necessary. They could just as validly go around the entire string ('--host=dbhost.com') or arbitrarily around any part of the argument (--hos't='dbhost.com).

If you want to read more about how all this works in detail you can read the Shell Command Language section of the POSIX spec.

Upvotes: 3

Related Questions