为什么此Shell脚本仅对一个实例有效,而对另一个实例无效?

时间:2018-11-27 06:24:55

标签: bash shell heredoc

我需要对以下代码进行一些解释,因为它们是相同的概念,但工作原理不同。因此,我正在尝试执行以下操作:

#!/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

这是我得到的输出:

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

我尝试了以下方法:

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

2 个答案:

答案 0 :(得分:3)

您大部分步骤都做对了,但是忘记了here-doc item.ModuleName中的命令替换构造。不这样做将使命令在本地外壳而不是远程主机中扩展。

另外,在运行$(..)命令时,转义的find会将文字字符串传递给它不理解的\$relative命令,即在本地计算机上发生以下情况

find

因此,您需要转义整个命令替换结构,以将整个here-doc扩展移动到远程主机中。

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

或者完全使用heredocs的替代形式,允许您解释heredoc文本中的变量。只需将定界标识符引用为ssh -T username@host << EOF relative="\$HOME/Documents" command=\$(find "\$relative" -name GitHub) command2=\$(echo "\$relative") echo "HERE: \$command" echo "HERE: \$command2" EOF

'EOF'

答案 1 :(得分:2)

这里文档的内容被解析两次;一次是通过本地外壳程序,然后是远程外壳程序(通过ssh连接发送之后)。这两种解析的工作方式不同:第二种(远程)按常规shell规则播放,而第一种(本地)仅 则查看反斜杠,$表达式(例如变量和命令替换) )和反引号样式的命令替换。最重要的是,本地的根本不关注引号,因此,将诸如单引号引起来的内容对它的解析方式(直到到达远端)没有影响。

例如,在以下行中:

relative="$HOME/Documents"

$HOME在本地计算机上扩展到本地用户的主目录路径。我很确定您希望它在远程计算机上扩展到远程用户的主目录路径。为此,您必须转义$

relative="\$HOME/Documents"

下一行比较复杂:

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

在这里,第二个$被转义了(通常会保留它以便在远端进行解释),但第一个不是。这意味着整个$(find \$relative -name GitHub)命令替换都在本地计算机上运行。如果您运行:

find \$relative -name GitHub

...作为常规命令,您会得到错误“查找:‘$ relative’:没有这样的文件或目录”,因为转义可防止变量替换的发生。加上它在错误的计算机上。因此,再次需要转义$(顺便说一句,您还应该对变量引用加双引号):

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

下一行也有类似的问题,但是排序仍然成功。

无论如何,这里有两种可能的解决方案:要么在here文档中转义$个字符的 all ,要么在文档定界符周围加上引号,然后再转义它们中的任何一个都是因为这会跳过所有本地分析(查找结尾定界符除外):

#!/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

如果您不希望在本地计算机上扩展任何内容,则此方法更简单。