我目前在本地机器上计算一些环境变量$FOO
。现在,我有一个由一些命令组成的 SSH 脚本,其中一些命令使用环境变量$FOO
,该变量仅在本地机器上设置。
我想在远程机器上运行这些命令但我想替换当地的版本,$FOO
这样当 SSH 脚本在我的远程机器上运行时,它将使用本地计算的值$FOO
。我需要这样做,因为命令在远程机器上的运行方式取决于$FOO
我计算的本地值。
我知道你可以ssh user@remote < script.sh
在远程机器上运行本地 shell 脚本,但我不确定如何执行该过程,但我的当地的 $FOO
环境变量,如上所述。我不想在本地或远程计算机上配置 SSH,所以SendEnv
对我来说这不是一个选择。
使用本地环境变量来运行我的 shell 脚本中的那些命令的最佳方法是什么$FOO
?
答案1
我假设您知道哪个 shell 应该解释该脚本。此示例使用sh
:
# somewhat flawed
ssh user@remote "FOO='$FOO' exec sh" < script.sh
本地 shell 将进行替换,$FOO
因为它被双引号括起来了 (这是)。单引号代表远程 shell(即exec
指向最终 的原始远程 shell sh
)。
注意ssh
将整个命令作为细绳。它将在远程端进行解释。如果$FOO
扩展为包含一个或多个单引号的字符串,则整个命令可能会出现错误。代码注入是可能的。
要“解除”可能的单引号,您可以尝试这种方法:
# still somewhat flawed
FOOx="$(printf '%s' "$FOO" | sed "s|'|'\"'\"'|g")"
ssh user@remote "FOO='$FOOx' exec sh" < script.sh
在我的 Debian 9 中,它将 every '
in更改$FOO
为'"'"'
in $FOOx
。分析远程 shell 中的引用,以查看中心单引号是否会保留下来。
我可以在没有额外变量的情况下做到这一点:
# still somewhat flawed
ssh user@remote "FOO='$(printf '%s' "$FOO" | sed "s|'|'\"'\"'|g")' exec sh" < script.sh
它不像我希望的那样坚固。问题:
sed
我认为,如果输入的是不完整的行(没有尾随换行符),某些实现可能会打印完整的行(带有尾随换行符)。或者sed
可以忽略不完整的行。如果变量的内容不以换行符结尾,这一点非常重要。$()
删除所有尾随的换行符。
为了处理这些问题,我x
在 之前附加了一个换行符sed
。该工具(无论实现方式如何)都应该接受所有内容,它不应该添加任何内容。然后$()
只会删除多余的换行符。我x
在远程端删除了多余的换行符。像这样:
# foolproof (AFAIK)
ssh user@remote "FOO='$(printf '%sx\n' "$FOO" | sed "s|'|'\"'\"'|g")'; FOO=\"\${FOO%x}\" exec sh" < script.sh
笔记:
- 如果您的(本地)
$FOO
扩展为肯定不包含单引号的字符串,那么您可以使用第一个最简单的命令。 如果您的本地 shell 是 Bash,那么您可以用扩展名替换单引号,
'"'"'
如下所示:# foolproof (AFAIK), needs local Bash pattern="'" replacement="'\"'\"'" ssh user@remote "FOO='${FOO//$pattern/$replacement}' exec sh" < script.sh
这解决了单引号的问题,并且不使用
$()
或sed
,因此不会出现其他提到的问题。我使用了额外的变量来避免这个问题。