Shell Quoting Conundrum

by Brian K. Jones

I originally posted this on my blog, but thought the broader sysadmin community could benefit from the discussion:

Somebody on a mailing list asked a question about shell quoting. The quoting issue would not have been so difficult had it not been for the fact that it was a command that was to be run on the other end of an SSH connection, which adds a level of difficulty in interpreting what's going on. Here's the command he was trying to run:

ssh -t hostname 'sudo sh -c "echo \'test\' ; echo \'test\'" '

(of course, nobody wants to run echo as root, it's just an example representative of the problem at hand)

My understanding is that the person running this wants to see output from this command that looks like this:

'test'
'test'

Here's how I believe the command would need to look to get that output:

ssh -t hostname "sudo sh -c \"echo \'test\'; echo \'test\'\""

The key is starting with double quotes instead of single quotes, because single quotes fed to a bash shell is a toggle switch that turns shell expansion on and off. Until about 5 minutes ago, I had always had the rule "Don't put single quotes inside single quotes" in my brain as a rule of thumb to avoid confusion. After doing some checking (in the bash man page), turns out it's a bona fide RULE in bash. From the man page:

A single quote may not occur between single quotes, even when preceded by a backslash.

Whaddya know? I must've picked that up along the way and forgot about it.

If you think about the single quote as a toggle between 'expansion on' and 'expansion off', and NOT as a literal single quote, and then parse that first command again, you'll see that this is probably not going to work.

2 Comments

Grant Jacobs
2007-02-14 12:32:54
Another way of saying this is that (the outermost) single quotes are literal quotes and double quotes are interpretative quotes.
Dominic Mitchell
2007-02-21 00:08:00
The main thing is that you don't need that "sh -c". Or need to quote the argument to ssh.


ssh -t hostname sudo echo "\'test\'"