在远程 ssh 命令中传递变量

Posted

技术标签:

【中文标题】在远程 ssh 命令中传递变量【英文标题】:Passing variables in remote ssh command 【发布时间】:2011-03-19 21:12:54 【问题描述】:

我希望能够使用 ssh 从我的机器运行命令并通过环境变量 $BUILD_NUMBER

这是我正在尝试的:

ssh pvt@192.168.1.133 '~/tools/myScript.pl $BUILD_NUMBER'

$BUILD_NUMBER 设置在进行 ssh 调用的机器上,由于远程主机上不存在该变量,因此不会被拾取。

如何传递$BUILD_NUMBER 的值?

【问题讨论】:

与 Hudson 无关,已移除标签。 (Hudson 只是创建了变量) 【参考方案1】:

不计算单引号中的变量。使用双引号:

ssh pvt@192.168.1.133 "~/tools/run_pvt.pl $BUILD_NUMBER"

shell 将扩展双引号中的变量,而不是单引号。这将在传递给ssh 命令之前更改为您想要的字符串。

【讨论】:

【参考方案2】:

如果你使用

ssh pvt@192.168.1.133 "~/tools/run_pvt.pl $BUILD_NUMBER"

而不是

ssh pvt@192.168.1.133 '~/tools/run_pvt.pl $BUILD_NUMBER'

您的 shell 将在将命令字符串发送到远程主机之前插入 $BUILD_NUMBER

【讨论】:

如果有人必须使用单引号以使引号中包含的命令不被本地评估,那么他们应该使用“'$VARIABLE'”。示例:ssh pvt@192.168.1.133 '~/tools/run_pvt.pl "'$BUILD_NUMBER'"' 不知道 bash 对单引号和双引号的反应不同。谢谢! linux核心开发者必须下地狱 @goldstar,请注意,shell 中单引号和双引号行为之间的区别比 Linux 早了几十年。 PSA:如果您的字符串包含用户输入,这是一个非常糟糕的主意,并且可能会使您面临代码注入攻击。【参考方案3】:

转义变量以访问 ssh 会话之外的变量: ssh pvt@192.168.1.133 "~/tools/myScript.pl \$BUILD_NUMBER"

【讨论】:

这并没有达到问题的要求。 从shell的角度来看,'$FOO'等价于"\$FOO"。问题是“如何使用 SSH 传递 shell 变量?”。正如@PatrickTrentin 所述,这不是一个正确的答案,因为没有远程设置BUILD_NUMBER 环境变量。 我赞成这个,因为虽然它没有具体回答原始问题,但它回答了(至少对我而言)想到的下一个问题:对于我想要插入变量的远程命令,我还想抑制单个变量的插值呢?如果我想为循环插入一个变量,然后远程插入循环变量,这将很有帮助。【参考方案4】:

如前所述,您不需要在远程主机上设置环境变量。相反,您可以简单地在本地主机上进行元扩展,然后将值传递给远程主机。

ssh pvt@192.168.1.133 '~/tools/run_pvt.pl $BUILD_NUMBER'

如果真的想在远程主机上设置环境变量并使用,可以使用env程序

ssh pvt@192.168.1.133 "env BUILD_NUMBER=$BUILD_NUMBER ~/tools/run_pvt.pl \$BUILD_NUMBER"

在这种情况下,这有点矫枉过正,请注意

env BUILD_NUMBER=$BUILD_NUMBER 在本地进行元扩展 主持人 远程BUILD_NUMBER 环境变量将被使用 远程外壳

【讨论】:

【参考方案5】:

(据我所知,这个答案可能看起来不必要地复杂,但它在空格和特殊字符方面很容易扩展和健壮。)

您可以直接通过ssh 命令的标准输入和远程位置的read 输入数据。

在下面的例子中,

    索引数组(为方便起见)填充了要在远程端检索其值的变量的名称。 对于这些变量中的每一个,我们都会给ssh 一个以空结尾的行来给出变量的名称和值。 在shh 命令本身中,我们循环遍历这些行来初始化所需的变量。
# Initialize examples of variables.
# The first one even contains whitespace and a newline.
readonly FOO=$'apjlljs ailsi \n ajlls\t éjij'
readonly BAR=ygnàgyààynygbjrbjrb

# Make a list of what you want to pass through SSH.
# (The “unset” is just in case someone exported
# an associative array with this name.)
unset -v VAR_NAMES
readonly VAR_NAMES=(
    FOO
    BAR
)

for name in "$VAR_NAMES[@]"
do
    printf '%s %s\0' "$name" "$!name"
done | ssh user@somehost.com '
    while read -rd '"''"' name value
    do
        export "$name"="$value"
    done

    # Check
    printf "FOO = [%q]; BAR = [%q]\n" "$FOO" "$BAR"
'

输出:

FOO = [$'apjlljs ailsi \n ajlls\t éjij']; BAR = [ygnàgyààynygbjrbjrb]

如果你不需要export 那些,你应该可以使用declare 而不是export

真正简化的版本(如果您不需要可扩展性,只需处理一个变量等)如下所示:

$ ssh user@somehost.com 'read foo' <<< "$foo"

【讨论】:

【参考方案6】:

也可以通过 ssh 显式传递环境变量。它确实需要一些服务器端设置,所以这不是一个通用的答案。

在我的例子中,我想将一个备份存储库加密密钥传递给备份存储服务器上的一个命令,而无需将该密钥存储在那里,但请注意任何环境变量在ps 中都是可见的!在标准输入上传递密钥的解决方案也可以,但我发现它太麻烦了。无论如何,这里是通过 ssh 传递环境变量的方法:

在服务器上,编辑sshd_config 文件,通常是/etc/ssh/sshd_config,并添加一个AcceptEnv 指令来匹配您要传递的变量。见man sshd_config。就我而言,我想将变量传递给 borg 备份,所以我选择了:

AcceptEnv BORG_*

现在,在客户端上使用-o SendEnv 选项发送环境变量。以下命令行设置环境变量BORG_SECRET,然后将其标记为发送到客户端计算机(称为backup)。然后它在那里运行 printenv 并过滤 BORG 变量的输出:

$ BORG_SECRET=magic-happens ssh -o SendEnv=BORG_SECRET backup printenv | egrep BORG
BORG_SECRET=magic-happens

【讨论】:

您可以使用默认服务器端设置see my answer“走私”您的变量。要点是,默认的 OpenSSHd 配置包含 LC_* 作为允许发送的变量,因此只需使用 $LC_TvE_foo$LC_BORG_SECRET,只要确保您不会与内置变量“冲突”。【参考方案7】:

默认情况下,SSHD 上接受的环境变量列表包括LC_*。因此:

LC_MY_BUILDN="1.2.3" ssh -o "SendEnv LC_MY_BUILDN" ssh-host 'echo $LC_MY_BUILDN'
1.2.3

【讨论】:

引用sshd_conf manual,“默认是不接受任何环境变量。”似乎一些 Linux 发行版将 AcceptEnv LANG LC_* 添加到默认配置中,但我不会依赖这个。

以上是关于在远程 ssh 命令中传递变量的主要内容,如果未能解决你的问题,请参考以下文章

shell脚本中ssh到远程机子时,提示输入密码?用变量给出密码 要怎么做?请各位帮忙

是否可以在远程 ssh 命令中使用变量?

无法使用 Runtime.Exec 传递远程命令 (ssh)

Linux-两种ssh远程执行命令方式加载环境变量区别

ssh远程连接服务器执行命令

如何在远程主机上的詹金斯中传递 $BUILD_NUMBER