使用本地环境变量通过 SSH 在远程机器上运行 shell 脚本

使用本地环境变量通过 SSH 在远程机器上运行 shell 脚本

我目前在本地机器上计算一些环境变量$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,因此不会出现其他提到的问题。我使用了额外的变量来避免这个问题

相关内容