为什么“ssh -t”无法登录并运行命令?

为什么“ssh -t”无法登录并运行命令?

我在 Mac OS Big Sur 上运行虚拟 Ubuntu 16.04 Linux 实例(使用 vagrant)。我可以很好地运行以下两个命令(一个用于 ssh 进入虚拟服务器,第二个用于在其中运行命令)...

$ ssh myvirtual.local
Warning: Permanently added '10.0.4.19' (ECDSA) to the list of known hosts.
Last login: Wed Sep 15 16:37:57 2021 from 10.0.4.1
$ foreman start -f Procfile.debug
16:38:26 rails.1      | started with pid 27884
16:38:26 worker.1     | started with pid 27885
16:38:26 scheduler.1  | started with pid 27887
...

我希望能够结合这两个命令,所以我尝试了

$ ssh -t myvirtual.local 'foreman start -f Procfile.debug’
Warning: Permanently added '10.0.4.19' (ECDSA) to the list of known hosts.
bash: foreman: command not found
Connection to 10.0.4.19 closed.

我对需要运行哪些其他设置来使用单个命令来模拟我最初所做的事情感到困惑。有什么想法吗?

答案1

这种情况下的主要区别在于,ssh host以登录模式启动登录 shell(在其前面添加前缀sshd,告诉 shell 必须通过读取、、、或(可能还有 中的系统启动脚本)来初始化登录会话,具体取决于在 shell 实现上),以及交互方式(因为它发出提示以获取您的输入)。这就是的模式。-argv[0].profile.bash_profile.zprofile.zlogin.login/etcrloginssh

在 中时ssh -t host 'some shell code',使用和作为参数sshd运行登录 shell ,但不告诉它是登录 shell。该 shell 也是非交互式的,不会从终端提示您输入代码。这就是模式。-csome shell codersh

在您的情况下,很可能找不到该命令,因为会话初始化文件中用于设置变量的某些指令$PATH尚未运行。

请注意,并非所有 shell 都可以同时作为登录 shell 和解释作为参数给出的代码。

外壳bash还可以。将在启动bash时作为登录 shell 启动,并且在传递或选项时启动,然后 1 仍然会读取登录会话初始化文件,即使传递了要执行的命令(POSIX 模式除外)。argv[0]--l--login-c

所以在这里,而不是这样做:

ssh -t host 'some code'

你可以这样做:

ssh -t host "exec bash --login -c 'some code'"

这将启动另一个bash,但这次是在解释 之前作为登录外壳(它将解释其中的代码,~/.bash_profile并可能解释初始化所在的/etc/profile位置)。/etc/profile.d$PATHsome code

或者您可以尝试找到进行定义的文件$PATH并执行以下操作:

ssh -t host '. /path/to/that/file && some code'

这仍然留下了另一个区别,即该 shell 不是交互式的,并且您的启动文件可能决定在交互时与非交互时表现不同(通过检查iin是否存在$-或通过检查变量是否存在$PS1)。

bash当作为登录 shell 运行时,即使在交互时(与大多数其他 shell 中发生的情况相反)启动时也不会解释~/.bashrc(交互式 shell 自定义文件),但大多数人通过source ~/.bashrc在其~/.bash_profile. Debian 及其衍生版本上的默认设置~/.profile默认情况下会这样做(当然仅当 shell 是时bash)。

bash默认情况下通过 ssh 调用时不会解释~/.bashrc,但可以在编译时使用配置选项进行配置SSH_SOURCE_BASHRC。 Debian 和 Ubuntu 等衍生产品确实可以实现这一点。因此,在执行操作时ssh ubuntu-host 'some code',如果您的登录 shell 在那台机器上是 bash,则将启动bash的 shell将在解释之前sshd进行解释。~/.bashrcsome code

所以~/.bashrc最终会在rloginrsh模式下被解释,但正如 @Quasimodo 所指出的,~/.bashrcDebian 和 Ubuntu 上的默认设置在顶部:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

$PATH因此,如果这些行下面有定义,它们只会在rlogin模式(其中bash是交互式、$-包含i并且~/.bashrc来源为~/.profile)中解释,而不是在rsh模式(其中~/.bashrc来源,但其大部分内容被跳过)中解释。 shell 不是交互式的)。


1 在以(与/相对)argv[0]开头的情况下,传递 a 时是否将其视为登录 shell取决于是否启用了编译。--l--login-c codebashNON_INTERACTIVE_LOGIN_SHELLS

答案2

您缺少环境设置,因为这两种情况并不总是等效:

ssh remoteHost
someCommand

ssh -t remoteHost someCommand

下一个命令块将为您提供首选解决方案中缺少的变量集(上面两个示例中的第二种格式)。您可以将这些添加到您的问题中,我将完成此问题以解释如何设置它们,或者您可以自己使用这些信息来设置它们。

ssh -t myvirtual.local env | grep = | sort >/tmp/env.1
( echo env; echo exit ) | ssh -tt myvirtual.local | grep = | sort >/tmp/env.2
comm -13 /tmp/env.1 /tmp/env.2

我这里的系统的简短示例输出

EDITOR=vi
HISTCONTROL=ignoredups
LESSCLOSE=/usr/bin/lesspipe %s %s
LESSOPEN=| /usr/bin/lesspipe %s
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:...
PAGER=less
PATH=/home/roaima/bin:/usr/local/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/bin:/bin
PROMPT_COMMAND_WAS=echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"
SHLVL=1
SSH_CLIENT=192.1.1.9 31996 22
SSH_CONNECTION=192.1.1.9 31996 192.1.1.18 22
TTY=/dev/pts/0
XDG_SESSION_ID=c61

这没有任何特定于 Ruby 或 Rails 的内容,但它确实表明在我的例子中变量PATH是不同的(您将能够在/tmp/env.1和中看到实际值/tmp/env.2)。

一旦您对此进行跟踪和排序,您可能需要foremanscreen或下运行,以便在连接终止时tmux它不会收到信号。但我建议您在引入这些附加工具之前先解决最初的问题。SIGHUPssh

相关内容