Why does this shell script work for one instance and not the other? [duplicate]

徘徊边缘 提交于 2021-02-10 13:17:14

问题


I'm in need of some explanation of the following code, because they are the same concepts but not working the same. So, I'm trying to do the following:

#!/bin/sh

ssh -T username@host << EOF

relative="$HOME/Documents"
command=$(find \$relative -name GitHub)
command2=$(echo \$relative)
echo "HERE: \$command"
echo "HERE: \$command2"

EOF

Here is the output I get:

find: ‘$relative’: No such file or directory 
HERE:
HERE: /home/username/Documents

I have tried the following:

"\$relative"
'\$relative'
"\${relative}"
"\$(relative)"

回答1:


You did the most parts right, but forgot to escape the command-substitution construct in the here-doc $(..). Not doing it will make the command expand in the local shell and not in the remote host.

Also while running the find command an escaped \$relative will pass the literal string to the find command which it does not understand, i.e. the following happens on the local machine

find \$relative
#   ^^^^ since $relative won't expand, find throws an error

So you need to escape the whole command-substitution constructs, to move the whole here-doc expansion in the remote host.

ssh -T username@host << EOF
relative="\$HOME/Documents"
command=\$(find "\$relative" -name GitHub)
command2=\$(echo "\$relative")
echo "HERE: \$command"
echo "HERE: \$command2"
EOF

Or altogether use an alternate form of heredocs that allows you to not interpret variables in the heredoc text. Simply quote the delimiting identifier as 'EOF'

ssh -T username@host <<'EOF'
relative="$HOME/Documents"
command=$(find "$relative" -name GitHub)
command2=$(echo "$relative")
echo "HERE: $command"
echo "HERE: $command2"
EOF



回答2:


The contents of the here-document are parsed twice; once by the local shell, then again by the remote shell (after it's sent over the ssh connection). These two parsings work differently: the second (remote) one plays by the usual shell rules, but the first (local) one only looks at backslashes, $ expressions (e.g. variable and command substitutions), and backtick-style command substitutions. Most importantly, the local one doesn't pay any attention at all to quotes, so doing something like putting single-quotes around something has no effect on how it gets parsed (until it gets to the remote end).

For example, in the line:

relative="$HOME/Documents"

$HOME gets expanded on the local computer, to the local user's home directory path. I'm pretty sure you want it to expand on the remote computer, to the remote user's home directory path. To do that, you have to escape the $:

relative="\$HOME/Documents"

The next line's a bit more complicated:

command=$(find \$relative -name GitHub)

Here, the second $ is escaped (which would normally preserve it for interpretation on the remote end), but the first isn't. That means that the entire $(find \$relative -name GitHub) command substitution gets run on the local computer. And if you run:

find \$relative -name GitHub

...as a regular command, you'll get the error "find: ‘$relative’: No such file or directory", because the escape prevents the variable substitution from ever happening. Plus it's on the wrong computer. So again you need to escape the $ (and BTW you should also double-quote the variable reference):

command=\$(find "\$relative" -name GitHub)

The next line has a similar problem, but sort-of succeeds anyway.

Anyway, there are two possible solutions here: either escape all of the $ characters in the here-document, or put quotes around the document delimiter, and then don't escape any of them because that skips all local parsing (except looking for the end delimiter):

#!/bin/sh

ssh -T username@host << 'EOF'

relative="$HOME/Documents"
command=$(find "$relative" -name GitHub)
command2=$(echo "$relative")
echo "HERE: $command"
echo "HERE: $command2"

EOF

If you don't want anything to expand on the local computer, this method's way simpler.



来源:https://stackoverflow.com/questions/53493901/why-does-this-shell-script-work-for-one-instance-and-not-the-other

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!