Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

linux - What difference does ssh command quoting make?

How is it that these commands result in different output?

?> ssh host bash -c 'cd /tmp; pwd'
/home/manu
?> ssh host "bash -c 'cd /tmp; pwd'"
/tmp

An answer to a similar question here states that:

The trailing arguments are combined into a single string which is passed to as an argument to the -c option of your login shell on the remote machine.

In case of an * I can understand this, but what part would be evaluated locally in this example? Also, an -- before the bash command doesn't help.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The thing to understand here is that ssh simply concatenates its arguments (in the same way that $* does), and passes that concatenated string to sh -c.


Thus, in the case of:

ssh josh-play bash -c 'cd /tmp; pwd'

...ssh runs:

sh -c 'bash -c cd /tmp; pwd'

...thus, sh runs:

bash -c cd /tmp
pwd

...and as you can test yourself, bash -c cd /tmp doesn't do much that's useful (the script text it runs consists only of cd; /tmp is stored as an argument, but the script text never reads its arguments, making that moot). Moreover, as soon as bash exits, we're back in the parent sh process, in which cd was never run.

The syntactic quotes passed to your outer shell are entirely lost, and the pwd is invoked not by the bash that you manually triggered (which, in this usage, simply invokes cd without any arguments -- /tmp is in $1, but the script passed as an argument to cd never dereferences that variable) but by the sh that ssh implicitly invokes.


If you know that your remote sh is provided by bash or ksh -- a shell supporting the $'' extension -- you can do the following for any arbitrary argv array (in this case bash, -c, cd /tmp; pwd):

# ask your shell to generate an eval-safe quoted form of your argument list
printf -v rmt_cmd '%q ' bash -c 'cd /tmp; pwd'

# pass that through to be run by the remote sh -c
ssh josh-play "$rmt_cmd"

The caveat to the above is that if your argument list can contain newlines or hidden characters, printf %q in bash or ksh can escape it in a form that POSIX sh isn't guaranteed to be able to read. To avoid that:

# ask your shell to generate an eval-safe quoted form of your argument list
printf -v rmt_cmd '%q ' bash -c 'cd /tmp; pwd'

# ...and use bash to evaluate that quoted form.
ssh josh-play 'exec bash -s' <<<"$rmt_cmd"

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...