ssh 是否在登录 shell 中运行命令(而不是 shell 本身)?

ssh 是否在登录 shell 中运行命令(而不是 shell 本身)?

根据man sshd

 LOGIN PROCESS
      When a user successfully logs in, sshd does the following:

      <...>

            9.   Runs user's shell or command.  All commands are run under the
                 user's login shell as specified in the system password data‐
                 base.

不过,尚不清楚“在用户的登录 shell 下运行”字面意思是“登录 shell,如bash -l”?我的实验表明,不,不是:

$ ssh u@h shopt -q login_shell && echo 'Login shell' || echo 'Not login shell'
Not login shell

我不明白为什么会这样?它导致运行的命令无法获得通常的环境,就像登录到 shell 一样。这很麻烦。

答案1

登录外壳在这种情况下可能意味着两个不同的事情:

  1. 被定义为帐户数据库中用户的登录 shell 的 shell。

    例如:

    $ getent passwd stephane
    stephane:*:1000:1000:Stephane Chazelas:/home/stephane:/bin/zsh
    

    /bin/zsh是我的登录 shell,是我在帐户数据库中输入的7 个字段。其他一些用户可能有/bin/tcsh、、、、.../bin/fish/bin/bash/sbin/nologin/bin/false

  2. 用于初始化登录会话的 shell 调用,通常由登录/sshd 完成...通过前缀 with argv[0]-某些 shell 也接受-lor--login选项)并告诉 shell 解释一些会话初始化文件如.profile.login.zlogin

无论如何,shinssh是为了和以前sshd一样rshd,总是调用 shell 来解释客户端发送的代码(如果有)。

如果你运行:

ssh user@host foo    bar

其中foobar参数被传递给sshssh用空格将它们连接起来,并将foo barshell 代码发送到服务器。

服务器host将运行登录外壳user3 个参数的:

  1. 登录 shell 的基本名称。就我而言zsh
  2. -c
  3. 客户给出的代码:foo bar在该示例中

shell 会将其解释为它自己的语法的代码,它不会被运行作为登录 shell,argv[0]不以-.

如果参数被传递给客户端,就像调用它时一样:

ssh user@host

然后rlogin进入该模式(就像在未传递命令时rsh运行的模式一样rlogin ),然后使用伪 tty 并仅使用一个参数( )sshd运行用户的登录 shell :argv[0]

  1. -在我的例子中,登录 shell 的基本名称以 a 为前缀-zsh

这告诉 shell 它正在作为登录 shell 运行,并且它将读取.zprofile // .profile.login

总结一下:

  • ssh host shell-code就像rsh host shell-code通过让远程用户的登录 shell 解析一些代码来远程运行命令。
  • ssh host类似于rlogin host,并通过在虚拟终端中以登录 shell 模式启动登录 shell 来远程登录。

1 对于这个非常简单的,除了/bin/false/sbin/nologin当然,也许对于喜欢使用/usr/bin/python3登录 shell 的用户来说,所有 shell 都会对代码进行相同的解释,但对于更复杂的 shell,会有一些变化。也可以看看如何在不知道远程用户的登录 shell 的情况下通过 ssh 执行任意简单命令?

答案2

不,SSH 仅在您登录到 shell 时调用登录 shell,而不是在运行命令时调用。这意味着如果您希望登录初始化文件运行(例如~/.profile),您需要明确执行此操作。这是一个经过深思熟虑的设计选择:在大多数登录都是在文本终端上的日子里,许多用户会在其中运行命令,.profile这些命令只有在登录交互式会话时才有意义。 (现在这种情况已经不太常见了,因为大多数人都在本地登录到图形会话。)您不会想要快速ssh example.com ls(或更糟糕的是rsync example.com:somefile .)来启动 Emacs 并打开调制解调器来获取电子邮件!


SSH 中的命令是一个字符串,通过将命令行参数与中间的空格连接起来而获得。例如ssh u@h shopt -q login_shell运行命令shopt -q login_shell。它完全等同于ssh u@h 'shopt -q login_shell'、 或ssh u@h 'shopt -q' login_shell等。

SSH 将该字符串传递到用户的登录 shell。 SSH 不知道任何其他 shell。服务器可能在非 Unix 系统上运行,或者在没有名为sh或 的程序的受限环境中运行bash

SSH 服务器运行用户数据库中列出的可执行文件作为用户的登录 shell,但不一定作为登录 shell。当命令行非空时,它传递一个三参数列表:

  • argv[0]是程序的基本名称,遵循通常的约定。
  • argv[1]是字符串-c
  • argv[2]是客户端传递的命令。

对于空命令行,SSH 服务器调用用户的登录 shell作为登录 shell。这意味着只有一个参数的列表:

  • argv[0]是一个短划线 ( -),后跟程序的基本名称。

ssh localhost 'cat /proc/$$/cmdline | tr \\0 \\n'例如,以下是Linux 计算机上的输出:

sh
-c
cat /proc/$$/cmdline | tr \\0 \\n

对于登录 shell,观察起来比较困难,因为登录 shell 本身通常会运行其他程序。查看发生情况的简单方法是暂时禁用登录 shell 的初始化文件,例如通过暂时重命名~/.profile(或~/.bash_profile~/.zprofile等,具体取决于您使用的 shell)。以下是使用帐户/bin/sh作为登录 shell 的示例:

$ mv ~/.profile ~/not.profile
$ ssh localhost
(/etc/motd content goes here)
Last login: Mon Apr 24 18:00:30 2023
$ cat /proc/$$/cmdline; echo
-sh
$ exit
Connection to localhost closed.
$ mv ~/not.profile ~/.profile

相关内容