如何将环境变量传递给新的 ssh 启动的登录会话?

kjo*_*kjo 6 ssh zsh environment-variables

假设在当前环境中FOO定义了一些变量(和export-ed)。让我称之为环境E0

现在,我使用ssh.

% ssh frobozz@for.example.com
Run Code Online (Sandbox Code Playgroud)

让我把这个新建立的会话的环境称为E1

通常FOO不会在 中定义E1,并且在特殊情况下,通常它的值将独立FOO于 in的值设置E0

我如何安排事情,以便在通过 登录时ssh,变量FOO被设置E1为它的值E0(理想情况下,也是export-ed E1)?

万一重要,我zsh同时使用E0E1

对于这个问题的答案,假设我对sshd运行在 上的进程的配置没有任何控制权for.example.com。这尤其排除了任何需要修改 的解决方案/etc/ssh/sshd_configfor.example.com

另外,假设 的值FOO不是常数;它将从一个登录会话更改为另一个登录会话。尤其是,FOO可能根本不设置 in FOO,这种情况下,效果E1类似于FOO=orexport FOO=或 or的效果就可以了unset FOO


更新:我试过这个

% ssh frobozz@for.example.com FOO=$FOO env
Run Code Online (Sandbox Code Playgroud)

...实际上,输出显示FOO已按指定设置。但是我还没有想出在启动登录会话时如何应用这个想法。具体来说,如果我跑

% ssh frobozz@for.example.com FOO=$FOO
Run Code Online (Sandbox Code Playgroud)

...该命令只是在没有建立连接的情况下返回(我认为它ssh解释FOO=$FOO为在远程主机上运行的“命令”)。另一方面,在我运行这个之后

% ssh frobozz@for.example.com FOO=$FOO zsh
Run Code Online (Sandbox Code Playgroud)

...我从来没有看到来自远程主机的 shell 提示;命令只是挂在那里。(但是,我确实ssh在此之前收到了密码提示。)最后一个命令的其他变体也会发生同样的事情,例如

% ssh frobozz@for.example.com FOO=$FOO /path/to/zsh
% ssh frobozz@for.example.com FOO=$FOO env zsh
% ssh frobozz@for.example.com env FOO=$FOO zsh
% ssh frobozz@for.example.com env FOO=$FOO zsh -l
% ssh frobozz@for.example.com 'env FOO=$FOO zsh'
Run Code Online (Sandbox Code Playgroud)

UPDATE2:实际上,看起来像“挂起”的结果是提示不可见。我仍然可以运行命令。此外,登录时运行的 zsh 初始化文件的顺序与正常情况不同,并且(也许并不奇怪)环境与正常情况有很大不同(并且非常重要,IOW 高于和超出FOO)。

UPDATE3:在 dave_alcarin 和 meuh 的帮助下,我发现这与我所追求的非常接近:

% ssh -t frobozz@for.example.com env FOO=$FOO zsh -l -s
Run Code Online (Sandbox Code Playgroud)

新环境和正常环境之间仍然存在一些重要差异,我需要理清,但这与我想要的非常接近。

Gil*_*il' 9

SSH 具有将环境变量从客户端传递到服务器的功能,但 OpenSSH 在默认服务器配置中禁用它。有一个例外:TERM,它在协议中有一个特殊的位置;但是通过这种TERM方式交流信息很尴尬,因为您必须确保客户端会对其进行解码。

尽管如此,一些服务器被配置为让一些环境变量通过。例如,Debian 将 SSH 服务器配置为允许名称以LC_. 通常这些是语言环境变量,但您可以放置​​任何您喜欢的内容。

如果服务器未配置为允许任何变量,您可以将它们作为命令的一部分传递,但有一些问题。您遇到的问题是,如果您将命令传递给ssh,则默认情况下不会在服务器上创建终端。这样ssh frobozz@for.example.com FOO=$FOO zsh做实际上运行一个壳,但壳被连接到配管,而不是一个端子,因此它不会显示提示或支撑命令行编辑。尽管如此,您还是可以输入命令——尝试输入ls Enter, 和exit EnterCtrl+D退出。解决方案是使用-t选项明确告诉 SSH 打开终端。

ssh -t frobozz@for.example.com FOO=$FOO zsh
Run Code Online (Sandbox Code Playgroud)

根据您的登录 shell 是什么,您可能想要运行

ssh -t frobozz@for.example.com FOO=$FOO exec zsh
Run Code Online (Sandbox Code Playgroud)

避免有一个额外的 shell 进程,它是显式调用的zsh.

您还没有遇到的第二个问题是引用。SSH 协议传输一个由远程shell 执行的字符串。在这里,您要传递由 组成的字符串FOO=,后跟 的值FOO,后跟一个空格和exec zsh。因此,的值FOO被解释为一段 shell 源代码,而不是存储在FOO变量中的字符串。如果值包含在 shell 语法中具有特殊含义的字符,这会有所不同。您需要添加额外的引用级别。假设你的本地 shell 是 zsh,你可以使用q参数扩展标志:

ssh -t frobozz@for.example.com FOO=${(q)FOO} exec zsh
Run Code Online (Sandbox Code Playgroud)

与普通情况的另一个区别是,您首先在远程端运行登录 shell,然后将其替换为非登录 shell。由于链以登录 shell 开始(这是无法避免的),因此您的.profileor.zprofile确实会运行;但是 zsh 的交互式实例不是登录 shell,这可能会有所不同,具体取决于 dot 文件的内容。最好的解决方案可能是避免在您的点文件中做脆弱的事情。您可以运行zsh -l,但这会运行第二个登录shell,这很容易产生更大的差异(尽管您也可以通过确保您的.profileor.zprofile是幂等的来解决这个问题)。