与非常相似获取 .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
读取的东西才知道。 ~/.bashrc
ZSH 是我的默认 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 配置完全混合,因此可以应用于任何所需的命令。