![ssh 是否在登录 shell 中运行命令(而不是 shell 本身)?](https://linux22.com/image/219630/ssh%20%E6%98%AF%E5%90%A6%E5%9C%A8%E7%99%BB%E5%BD%95%20shell%20%E4%B8%AD%E8%BF%90%E8%A1%8C%E5%91%BD%E4%BB%A4%EF%BC%88%E8%80%8C%E4%B8%8D%E6%98%AF%20shell%20%E6%9C%AC%E8%BA%AB%EF%BC%89%EF%BC%9F.png)
根据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
登录外壳在这种情况下可能意味着两个不同的事情:
被定义为帐户数据库中用户的登录 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
用于初始化登录会话的 shell 调用,通常由登录/sshd 完成...通过前缀 with
argv[0]
(-
某些 shell 也接受-l
or--login
选项)并告诉 shell 解释一些会话初始化文件如.profile
、.login
、.zlogin
等
无论如何,sh
inssh
是为了壳和以前sshd
一样rshd
,总是调用 shell 来解释客户端发送的代码(如果有)。
如果你运行:
ssh user@host foo bar
其中foo
和bar
参数被传递给ssh
,ssh
用空格将它们连接起来,并将foo bar
shell 代码发送到服务器。
服务器host
将运行登录外壳有user
3 个参数的:
- 登录 shell 的基本名称。就我而言
zsh
-c
- 客户给出的代码:
foo bar
在该示例中
shell 会将其解释为它自己的语法的代码,它不会被运行作为登录 shell,argv[0]
不以-
.
如果不参数被传递给客户端,就像调用它时一样:
ssh user@host
然后rlogin
进入该模式(就像在未传递命令时rsh
运行的模式一样rlogin
),然后使用伪 tty 并仅使用一个参数( )sshd
运行用户的登录 shell :argv[0]
-
在我的例子中,登录 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