ssh <host>是登录shell,但是ssh <host> <command>不是吗?


12

我注意到,当我使用ssh <host> <command>语法直接在SSH主机上运行命令时,看到的.bashrc.bash_profile(或.profile)的输出,但看不到。

例如,如果我将以下命令放在两个文件的顶部,

echo ${BASH_SOURCE[0]}

和手动来源.bash_profile.bashrc依次来源),我将看到

$ . .bash_profile
.bash_profile
.bashrc

如果使用ssh <host>命令的形式通过SSH远程登录到此计算机,则这与我看到的输出相同。(如果我.bash_profile暂时存放在其他地方,则这两行都不会被回显。)

但是,如果我直接在远程计算机上以的ssh <host> <command>形式执行命令ssh,则输出如下所示:

$ ssh <host> echo foo
/home/rlue/.bashrc
foo

我的理解是.bash_profile和之间的区别.bashrc是,前者用于登录shell,而后者用于交互式非登录shell

我得出以下结论:

  1. ssh <host>仅来源.bash_profile,而
  2. ssh <host> <command>仅来源.bashrc,这意味着
  3. 前者是登录外壳,而后者不是。

这些结论正确吗?为什么将其ssh <host> <command>视为交互式非登录外壳?SSH是否仍然登录到远程计算机以执行命令?


输出.bashrc?该文件不应产生任何输出。.bashrc使用ssh作为传输工具的任何输出都可能破坏所有工具。
kasperd '17

很公平。在这种情况下,有几行出现.bashrc了错误,而.bash_profile没有出现类似的行。在修正违规行为之前,我借此机会调查了差异。
瑞安·吕

Answers:


12

OpenSSH(很可能是您正在运行的内容)决定是否创建登录外壳,并且只有在未运行特定命令时才这样做。来自man ssh

 If command is specified, it is executed on the remote host instead of a
 login shell.

因此,无论是否要创建登录shell,这都是ssh服务器的一种实现选择,并且如果您提供要运行的命令,则不是。

虽然ssh确实执行登录,但是如果要让它执行命令并退出,则与获取登录环境相比,它实际上更类似于创建仅用于运行该命令的shell。有鉴于此,编写OpenSSH的人们似乎决定将其视为此类任务。

他们创建了一个非交互式,非登录外壳程序来执行命令,因为这是在另一个上下文/外壳程序中运行命令的精神。但是,通常情况下,非交互式shell不会自动获得源代码~/.bashrc,这显然在这里发生。 bash实际上是在帮助我们。来自文档

由远程Shell守护程序调用

Bash尝试确定它何时在其标准输入连接到网络连接的情况下运行,就像由远程Shell守护程序(通常为rshd或安全Shell守护程序sshd)执行时一样。如果Bash确定它是以这种方式运行的,则它从〜/ .bashrc读取并执行命令(如果该文件存在并且可读)。如果作为sh调用,它将不会执行此操作。--norc选项可用于禁止此行为,而--rcfile选项可用于强制读取另一个文件,但是rshd和sshd通常都不会使用这些选项来调用shell或允许指定它们。


“ ...在远程主机而不是登录shell上执行。” 我没有这种二分法。命令是在远程主机上而不是在登录shell中执行,还是命令在远程主机上而不是在其中执行登录shell?如果是前者,它是不是?(通常不是都这样吗?)如果是后者,它仍然在某些 shell 的上下文中执行,不是吗?(一个交互式的非登录帐户?)所以我的问题也是关于语义的-“登录外壳”是什么意思,为什么OpenSSH不会设计成不为单个命令创建一个?
瑞安·吕

@RyanLue不同的外壳“风味”使某些任务更容易/更安全/更优化等。尽管这样做ssh确实需要登录,但实现者显然认为在某些情况下,例如,要求它执行命令并返回,不需要/无益于登录shell采取的额外步骤,因此他们跳过了这一步。因此,确实有一个正在运行的外壳程序,我主要假设是要设置环境,并且由于该外壳程序将不提供给登录用户,因此他们将其视为用户刚刚启动一个新的外壳程序来运行该环境。命令
Eric Renouf

“所以确实有一个正在运行的外壳程序,我想主要是为了设置环境...” <但我只是对此进行了试验,它似乎ssh <host> <command>没有继承任何现有登录外壳程序的环境。例如,$ ssh <host> \$PATH按原样返回路径而无需采购.bash_profile(或.profile原样)...从实际意义上讲,您为什么要绕过该步骤?
瑞安·吕

“ ...实施者显然认为在某些情况下(例如,要求其执行命令并返回命令)不需要/无益于登录shell采取的额外步骤,因此他们跳过了。” <另外,专门寻求对此设计选择的澄清/见解。我定义了我的意思PATH.profile这不是在远程主机上运行任意命令之前要加载的东西吗?
瑞安·吕

1
@RyanLue Boks ssh服务器可以区分ssh的不同用途,并可以为每一个授予权限。远程登录,远程执行,远程复制,也许可以帮助您理解为什么会有您描述的行为。在高度安全的环境中,远程登录(交互使用)可能要求太多。可以通过在.bash_profile或chroot中使用rbash / rksh和'logout'来限制Openssh。
bbaassssiiee

4

此行为的原因所在的层次比shell低:(ssh host“ login shell”情况)使用远程主机上的伪终端sshd服务器进程和Shell 之间进行通信;ssh host commandsshd和之间使用管道command,而不是。伪终端对于交互使用命令解释器(例如shell)或脚本语言的“ read-eval-print ”模式是必不可少的。他们实现了许多对人类友好的功能,例如能够按错字退格。但是它们具有更多的开销,并且(取决于配置)不允许任意数据未经修改地传递,因此SSH避免在不进行交互时避免使用它们。

有时SSH的命令/无命令启发式方法会导致此错误;可以使用-t-T开关覆盖它。例如,要登录到远程计算机并立即重新挂起挂起的screen会话,您需要执行ssh -t host screen -Rssh host screen -R将导致screen抱怨没有连接到终端。我想不出您真正使用的情况-T,但是只要您找到它,它就会在那里。


1

首先,您需要查看不同的类型,您可以阅读以下内容:

/unix/170493/login-non-login-and-interactive-non-interactive-shells

现在,如果您打开bashrc,将在开始时看到以下内容:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

这意味着根据您访问系统的方式,此文件是否在其中加载代码。


好的,但这提出了一个有趣的问题:.bashrc如果在非交互式上下文中调用它(如果没有“提示语句” / $PS1变量)则可能被编写为不被获取。但ssh <host> <command> 绝对是非交互的;也就是说,它并没有提出一个命令提示符。那么,为什么.bashrc针对这种看似登录,非交互的用例,将OpenSSH设计为创建一个非登录,交互的提示(尝试提供源代码)?
瑞安·吕
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.