假A终端

假A终端

与非常相似获取 .bashrc 后,使用 ssh 在交互式 shell 中运行命令,但那里的答案对我不起作用。

我想在完整的交互式 shell 下通过 ssh 执行远程命令。即,
在登录 shell 下运行带有一些参数的远程命令。

基本上,我需要以下两种情况才能工作:

ssh user@remote_computer -t bash -l -c '/bin/echo PATH is $PATH'
ssh user@remote_computer -t 'bash -l -c "java -version"'

java: command not found但目前我从案例 1 和案例 2 中得到了一个空行:

$ ssh user@remote_computer -t bash -l -c '/bin/echo PATH is $PATH'

Connection to remote_computer closed.

$ ssh user@remote_computer -t bash -l -c 'true; /bin/echo PATH is $PATH'
PATH is /usr/local/bin:/usr/bin:/bin:/usr/games
Connection to remote_computer closed.

ssh user@remote_computer -t bash -l -c 'true; java -version'
bash: line 1: java: command not found
Connection to remote_computer closed.

更新:

至少有两个人认为这是一个引用问题,但是请任何人解释一下为什么这是一个引用问题,以及我怎样才能得到我上面想要的东西。例如,这也是我尝试过的:

ssh user@remote_computer -t 'bash -l -c "java -version"'
bash: line 1: java: command not found
Connection to remote_computer closed.

我已经不知道如何以不同的方式引用它了。请帮忙!

如果我在之后运行相同的命令ssh user@remote_computer,我会得到:

$ bash -l -c "java -version"
openjdk version "11.0.15" 2022-04-19 LTS
OpenJDK Runtime Environment Zulu11.56+19-CA (build 11.0.15+10-LTS)
OpenJDK 64-Bit Server VM Zulu11.56+19-CA (build 11.0.15+10-LTS, mixed mode)

所以这对我来说显然不是一个引用问题。

答案1

-c通过 SSH 的shell命令在这里是有问题的;有一些棘手的引用问题,我不清楚谁在什么时间运行。这里 只有出于测试目的而customjava读取的东西才知道。 ~/.bashrcZSH 是我的默认 shell。

$ ssh -t localhost bash -c 'source ~/.bashrc;customjava -version'
/home/jhqdoe/.bashrc: line 0: source: filename argument required
source: usage: source filename [arguments]
zsh:1: command not found: customjava
Connection to 127.0.0.1 closed.
$ ssh -t localhost bash -c ':;source ~/.bashrc;customjava -version'
java version 99999
Connection to 127.0.0.1 closed.

我想你可以通过strace一些事情,ssh -v -v -v并且可能通过源代码深入了解上述两个命令之间的区别(:是空命令,并且比它少打字 true),但如果事情已经如此脆弱且难以调试,我会寻找一些其他解决方案。我的偏好通常是引用整个命令:

$ ssh localhost 'bash -ic "customjava -version"'
java version 99999

然而,如果命令中涉及更复杂的引用需求和变量替换,这可能会变得太复杂。 (复杂的 shell 引用不是我想要在凌晨 2 点调试的事情,所以我倾向于默认避免它。)

代替管道

另一种方法是将命令通过管道传输到所需的 shell;这最大限度地减少了运行命令的复杂性,使之成为可能可选的 shell 调用:

$ printf 'customjava -version'"\n" | ssh localhost 'bash -i'
bash$ customjava -version
java version 99999
bash$ exit
$ printf 'customjava -version'"\n" | ssh localhost 'bash -l'
java version 99999

这里的缺点是标准输入不是终端,因此如果另一端的命令确实需要终端,则这将不起作用。可能会有关于此的警告。

$ printf 'customjava -version'"\n" | ssh localhost
Pseudo-terminal will not be allocated because stdin is not a terminal.
zsh: command not found: customjava
$ printf 'customjava -version'"\n" | ssh -t localhost
Pseudo-terminal will not be allocated because stdin is not a terminal.
zsh: command not found: customjava

假A终端

如果需要终端,我可能会切换到expect;这将创建一个假终端,命令将在其中运行:

#!/usr/bin/env expect

spawn -noecho ssh localhost

# assume a prompt containing at least "% " (ZSH)
expect "% "

# replace ZSH with another shell
send -- "exec bash\n"
# assume a prompt of at least "$ "
expect "$ "
send -- "customjava -version\n"
expect "$ "
set version_info $expect_out(buffer)
send -- "exit\n"

puts "got >>>$version_info<<<"

但这还有其他问题,特别是错误检查和检测 shell 提示符的问题(例如,有人可能会摆弄,因此要做的第一件事可能是将提示符设置为某个已知值以 expect进行匹配)。如果有人以无数种方式破坏交互式 shell 配置,它也可能崩溃。也许跑步 /bin/sh并希望不是bash?但随后您可能需要配置sh自定义 java。这导致...

从 Shell 中删除配置

解决此问题的另一种方法是在 SSH 服务器上编写一个特殊命令(可能是一个 exec 包装器),它可以正确配置环境,然后运行该java命令或任何命令。然后你可以运行setup-our-env bash(一个供人类使用的交互式 shell)或 setup-our-env java -version(一个通过 SSH 运行的简单命令,而无需交互式 shell 的复杂化)。 exec 包装器可以简单如下:

#!/bin/sh
PATH=/custom/java/bin:$PATH
exec "$@"

换句话说,自定义 java 版本的环境设置不会与交互式 shell 配置完全混合,因此可以应用于任何所需的命令。

相关内容