Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does bash deal with nested quotes? [duplicate]

I need to run a command with a syntax like this:
runuser -l userNameHere -c '/path/to/command arg1 arg2'

Unfortunately, I have to nest additional ' characters into the command itself and I can't tell bash to interpret these correctly. The command I would like to run is actually:

runuser -l miner -c 'screen -S Mine -p 0 -X eval 'stuff "pwd"\015''

Unfortunately, bash seems to be hitting the second ' and puking. This is the error:
-bash: screen -S Mine -p 0 -X eval stuff: No such file or directory, so obviously it's not getting past the '.

How can I nest this as one command? Thank you!

like image 653
user1833028 Avatar asked Sep 19 '14 19:09

user1833028


People also ask

How do you pass a double quote in Bash?

Single quotes(') and backslash(\) are used to escape double quotes in bash shell script. We all know that inside single quotes, all special characters are ignored by the shell, so you can use double quotes inside it. You can also use a backslash to escape double quotes.

How do you escape double quotes in double quotes?

' appearing in double quotes is escaped using a backslash. The backslash preceding the ' !

How do you handle double quotes?

If you need to use the double quote inside the string, you can use the backslash character. Notice how the backslash in the second line is used to escape the double quote characters. And the single quote can be used without a backslash.


Video Answer


2 Answers

The command itself doesn't see the outer quotes. The shell uses those to avoid wordsplitting, but doesn't let the command know they were there. So if you have:

./command foo bar "baz biz"

The command sees three args, foo, bar and baz biz with whitespace intact, but no quotes. Thus, if you need to actually send quotes, you can do that by wrapping the argument with the other kind of quote:

./command "foo'bar"

The command sees one arg: foo'bar. But if you need to send both kinds of quotes, you have a harder problem to solve. You can solve it with leaning toothpicks, quote-swapping or variables:

Quote swapping

Even though the shell uses quotes to avoid wordsplitting, if you put the quoted arguments next to each other without whitespace the command will see it as one word:

./command "foo""bar"

The command sees one arg: foobar. So if you use two different kinds of quotes:

./command 'foo"bar'"baz'quux"

The command sees one arg: foo"barbaz'quux.

Leaning toothpicks

There are two kinds of leaning toothpicks. One is really just quote swapping except you don't use quotes to wrap one of the …quotes.

./command 'foo"barbaz'\'quux

The command sees one arg: foo"barbaz'quux. The other is (hat tip: chepner) to use the special $'string' form of word expansion, which allows ANSI C strings:

./command $'foo"barbaz\'quux'

The command sees one arg: foo"barbaz'quux.

Variables

doublequote=\" singlequote=\'
./command "foo${doublequote}barbaz${singlequote}quux"

The command sees one arg: foo"barbaz'quux.

like image 104
kojiro Avatar answered Oct 20 '22 15:10

kojiro


You can use another type of quoting supported by bash, $'...'. This can contain escaped single quotes.

runuser -l miner $'screen -S Mine -p 0 -X eval \'stuff "pwd"\015\''

Note that within $'...', the \015 will be treated replaced with the actual ASCII character at codepoint 015, so if that's not what you want, you'll need to escape the backslash as well.

runuser -l miner $'screen -S Mine -p 0 -X eval \'stuff "pwd"\\015\''

I think you can take advantage of the $'...' to remove the need for eval as well:

runuser -l miner $'screen -S Mine -p 0 -X stuff "pwd"\015'
like image 14
chepner Avatar answered Oct 20 '22 13:10

chepner