登录 Shell 和非登录 Shell 的区别?

Igo*_*rio 435 shell login

我了解交互式 shell 和非交互式 shell 之间的基本区别。但是,登录 shell 与非登录 shell 的确切区别是什么?

你能举例说明非登录交互式shell 的使用吗?

Gil*_*il' 409

当您登录交互式会话时,登录 shell 是在您的用户 ID 下执行的第一个进程。登录过程告诉 shell 作为一个登录 shell 使用一个约定:传递参数 0,它通常是 shell 可执行文件的名称,-前面带有一个字符(例如-bash,通常是bash。登录 shell 通常读取一个文件之类的设置环境变量:/etc/profile~/.profile为传统Bourne shell的,~/.bash_profile另外对于bash /etc/zprofile~/.zprofile为zsh的/etc/csh.login~/.login为CSH等。

当您在文本控制台或通过 SSH 或使用登录时su -,您将获得一个交互式登录shell。当您以图形模式(在X 显示管理器上)登录时,您不会获得登录外壳,而是获得会话管理器或窗口管理器。

很少运行非交互式登录shell,但是当您使用显示管理器登录时,某些 X 设置会这样做,以便安排读取配置文件。其它设置(这取决于上的分布和显示管理器)中读取/etc/profile~/.profile明确的,或者不读它们。获得非交互式登录 shell 的另一种方法是使用通过标准输入传递的命令远程登录,该命令不是终端,例如ssh example.com <my-script-which-is-stored-locally(与ssh example.com my-script-which-is-on-the-remote-machine运行非交互式、非登录 shell 的 相比)。

当您在现有会话(屏幕、X 终端、Emacs 终端缓冲区、另一个内部的 shell 等)的终端中启动 shell 时,您将获得一个交互式的非登录shell。该外壳可能会读出壳配置文件(~/.bashrc对于bash援引为bash/etc/zshrc~/.zshrc用于zsh的,/etc/csh.cshrc~/.cshrc用于CSH,该文件通过指定ENV对POSIX / XSI兼容外壳,例如仪表板,KSH,和bash可变时作为调用sh$ENV如果设置和~/.mkshrc对于 mksh 等)。

当 shell 运行脚本或在其命令行上传递的命令时,它是一个非交互式、非登录的shell。这样的 shell 一直运行:当一个程序调用另一个程序时,它实际上在 shell 中运行一个小脚本来调用另一个程序是很常见的。在这种情况下,一些 shell 读取启动文件(bash 运行由BASH_ENV变量指示的文件,zsh 运行/etc/zshenv~/.zshenv),但这是有风险的:shell 可以在各种上下文中调用,几乎没有任何你可以做的事情打破东西。

我正在简化一点,有关血腥的详细信息,请参阅手册。

  • @PiotrDobrogost `echo $- | bash -lx` (14认同)
  • @KevinWheeler 在 OSX 上,默认情况下,终端应用程序运行登录 shell。(正如我所解释的,启动 shell 的程序决定了 shell 是否充当登录 shell。)这不是正常的做事方式。 (7认同)
  • @IAmJulianAcosta 如果`FOO` 是一个环境变量(即`.profile` 包含`export FOO=something`),那么它可用于所有子进程,包括`foo.sh`。如果你把 `.profile` 改成 `export FOO=something_else` 那么 `./foo.sh` 仍然会打印 `something` 直到你下次登录。 (3认同)
  • 你能举例说明如何将`bash`作为非交互式登录shell运行吗? (2认同)
  • 我不知道这是否是真的,但我想注意,当我打开一个新终端(在 osx 上使用默认设置)时,即使我从未输入我的用户名或密码,我也会得到一个登录 shell。 (2认同)

Tim*_*iam 69

要判断您是否在登录 shell 中:

prompt> echo $0
-bash # "-" is the first character. Therefore, this is a login shell.

prompt> echo $0
bash # "-" is NOT the first character. This is NOT a login shell.
Run Code Online (Sandbox Code Playgroud)

在 Bash 中,您还可以使用shopt login_shell

prompt> shopt login_shell
login_shell     off
Run Code Online (Sandbox Code Playgroud)

(或on在登录外壳中)。

信息可以在man bash(搜索调用)中找到。这是摘录:

登录 shell 是参数零的第一个字符是 - 或以 --login 选项开头的 shell。

你可以自己测试一下。无论何时您使用 SSH,您都在使用登录 shell。例如:

prompt> ssh user@localhost
user@localhost's password:
prompt> echo $0
-bash
Run Code Online (Sandbox Code Playgroud)

使用登录 shell 的重要性在于,任何设置都/home/user/.bash_profile将被执行。如果您有兴趣,这里有更多信息(来自man bash

“当 bash 作为交互式登录 shell 或作为带有 --login 选项的非交互式 shell 调用时,它首先从文件 /etc/profile 中读取并执行命令(如果该文件存在)。读取该文件后,它以该顺序查找~/.bash_profile, ~/.bash_login, 和~/.profile, 并从第一个存在且可读的命令中读取和执行命令。启动 shell 时可以使用 --noprofile 选项来禁止这种行为。”

  • 在 bash 手册中,“登录 shell 是一个参数零的第一个字符是“-”的,或者是使用 --login 选项调用的。根据这个定义,`echo $0` 无法确定一个 shell 是否在登录 shell 中,因为当我们用 `bash --login` 启动一个 shell 时,`$0` 被设置为 `bash`(文件名用于调用Bash),而不是`-bash`。 (2认同)

小智 24

在登录 shell 中,argv[0][0] == '-'. 这就是它如何知道它是一个登录shell。

然后在某些情况下,它的行为取决于其“登录外壳”状态。例如,不是登录shell 的shell 不会执行“注销”命令。

  • 根据`man bash`,加上强调,*“登录shell是一个参数零的第一个字符是-,**或一个以--login选项开头的shell。**”* (5认同)

Jul*_*ian 21

在 GUI 中的新终端中启动的 shell 将是交互式非登录 shell。例如,它会提供您的 .bashrc,而不是您的 .profile。


小智 14

我将详细说明 Gilles 的出色回答,并结合 Timothy 的检查登录 shell 类型的方法。

如果您喜欢亲自查看事物,请尝试下面的片段和场景。

检查 shell 是否是(非)交互式的

if tty -s; then echo 'This is interactive shell.'; else echo 'This is non-interactive shell.'; fi
Run Code Online (Sandbox Code Playgroud)

检查 shell 是否是(非)登录

如果输出以echo $0开头-,则是登录 shell(echo $0输出示例:)-bash。否则它是非登录 shell(echo $0输出示例:)bash

if echo $0 | grep -e ^\- 2>&1>/dev/null; then echo "This is login shell."; else echo "This is non-login shell."; fi;
Run Code Online (Sandbox Code Playgroud)

让我们将以上两者结合起来,同时获取两条信息:

THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; 
THIS_SHELL_LOGIN_TYPE='non-login'; 
if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; 
if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi;
echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"
Run Code Online (Sandbox Code Playgroud)

场景:

没有特殊选项的典型 SSH 会话

ssh ubuntu@34.247.105.87
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-1083-aws x86_64)

ubuntu@ip-172-31-0-70:~$ THIS_SHELL_INTERACTIVE_TYPE='non-interactive';
ubuntu@ip-172-31-0-70:~$ THIS_SHELL_LOGIN_TYPE='non-login';
ubuntu@ip-172-31-0-70:~$ if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi;
ubuntu@ip-172-31-0-70:~$ if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi;
ubuntu@ip-172-31-0-70:~$ echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"

interactive/login
Run Code Online (Sandbox Code Playgroud)

运行脚本或通过新 shell 显式执行

ubuntu@ip-172-31-0-70:~$  bash -c 'THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; THIS_SHELL_LOGIN_TYPE='non-login'; if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi; 
echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"'

interactive/non-login
Run Code Online (Sandbox Code Playgroud)

远程运行本地脚本

ssh ubuntu@34.247.105.87 < checkmy.sh
Pseudo-terminal will not be allocated because stdin is not a terminal.
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-1083-aws x86_64)

non-interactive/login
Run Code Online (Sandbox Code Playgroud)

通过 ssh 远程运行命令

ssh ubuntu@34.247.105.87 'THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; THIS_SHELL_LOGIN_TYPE='non-login'; if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi; echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"'

non-interactive/non-login
Run Code Online (Sandbox Code Playgroud)

使用-tswitch通过 ssh 远程运行命令

当您想使用-tswitch通过 ssh 远程运行命令时,您可以明确请求交互式 shell 。

ssh ubuntu@34.247.105.87 -t 'THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; THIS_SHELL_LOGIN_TYPE='non-login'; if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi; echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"'

interactive/non-login
Run Code Online (Sandbox Code Playgroud)

注意:关于为什么远程运行命令不是这里的login shell更多信息。


小智 6

这是旧线程,但我刚刚找到了非登录交互式 shell 的具体示例。

当我在 Linux VM 上使用 VSCode 进行远程开发时,我意识到/etc/profile.d/env_file.sh即使重新启动 VSCode 和终端本身,VSCode 集成终端也没有获取我的环境变量。输出$0表明它不是登录 shell。

看起来,在连接到远程 Linux 计算机后,VSCode 只是启动了一个主登录 shell,但对于每个集成终端,它只是启动了另一个/bin/bash进程。ps您会看到does not have a的输出-,但这并不能决定当前 shell 是否登录。

$ echo $0
/bin/bash
$ ps  $$
  PID TTY      STAT   TIME COMMAND
 2274 pts/3    Ss     0:00 /bin/bash
Run Code Online (Sandbox Code Playgroud)

当我直接通过 SSH 登录服务器时,我看到-bash,这是一个登录 shell。

~$ echo $0
-bash
~$ ps  $$
  PID TTY      STAT   TIME COMMAND
 2088 pts/2    Ss     0:00 -bash
Run Code Online (Sandbox Code Playgroud)

然后我-l在 VSCode 中添加了 shell 参数 ( ) 选项。现在,echo 的输出$0是相同的,但请注意$$(当前进程的 ID,PID)/bin/bash带有-l( --login) 选项。

$ echo $0
/bin/bash
$ ps  $$
   PID TTY      STAT   TIME COMMAND
  2309 pts/3    Ss     0:00 /bin/bash -l
Run Code Online (Sandbox Code Playgroud)

因此,要检查 shell 是否是登录 shell,您需要检查echo $0ps $$。根据实现,输出应该是-bash或者/bin/bash -l


ade*_*hox 6

吉尔斯的回答很好,但对我来说很难理解,所以这是我个人需要用更简单的语言了解的要点。

  • 登录shell是用户登录时提供给用户的 shell。所以用户登录后只会存在其中之一。

  • 登录 shell是在不受登录过程干扰的情况下调用的 shell。非登录 shell 不需要每次登录都有一个,因此登录后您可能会获得任意数量的非登录 shell。

两者之间的区别允许用户每次登录只需要执行一次的任务(例如一些繁重的任务)绑定到登录 shell 的启动,并避免在创建每个新的非登录时触发它们shell(他们可能需要很多个)。通过下面的两个例子,事情会变得更加清晰(希望如此)。

示例1:

首先创建一个新用户(之后不要切换到它):

sudo adduser foo
Run Code Online (Sandbox Code Playgroud)

然后成为根:

sudo -i
Run Code Online (Sandbox Code Playgroud)

并将此行添加到/home/foo/.profile

echo "Echoed on start of $USER's login shell"
Run Code Online (Sandbox Code Playgroud)

现在使用 su 切换新用户,如下所示(猜猜首先会发生什么):

su foo
Run Code Online (Sandbox Code Playgroud)

你不会看到任何回声。你可能期望你添加到文件中的echo/home/foo/.profile能够运行,因为这个文件是一个登录触发的文件,但问题不在于这个文件,问题在于你根本没有登录过foo(是的,虽然它提示了你输入 foo 的密码)。因此,尽管您可以使用该命令切换到其他用户su <username>,但这根本不是“登录”。只是切换后,每当你运行命令时,都会使用该用户的ID和组ID,而不是你的。即根据man su

su 允许使用替代用户和组 ID 运行命令。

那么,当切换到某个用户时,我们如何登录该用户(并获取该用户的登录 shell)呢?我们在 中读到man su我们可以使用其中一个选项-, -l, --login

-、-l、--login:将 shell 作为登录 shell 启动...

因此,此时如果您exit,您将不会退出“foo”,您将退出您自己的用户!因此exit,我们不再使用 ing,而是再次使用以下命令切换回我们自己的用户su

su <your_own_user_name>
Run Code Online (Sandbox Code Playgroud)

现在运行这个命令:

su --login foo
Run Code Online (Sandbox Code Playgroud)

并给出 foo 的密码。这次,您将看到这一行回显:

在 foo 的登录 shell 启动时回显

现在您也可以运行exit,注销 foo 并切换到您自己的登录 shell。

示例2:

将此行添加到您的~/.profile

echo "start of $USER's login shell"
Run Code Online (Sandbox Code Playgroud)

保存,然后打开一个新终端,你会看到它回显。现在运行:

echo $0
Run Code Online (Sandbox Code Playgroud)

您必须看到(假设您使用的是 Bash):

-bash
Run Code Online (Sandbox Code Playgroud)

如您所见,它是 shell 程序的名称,前面带有破折号,这意味着它是登录 shell。现在运行:

bash
Run Code Online (Sandbox Code Playgroud)

您不会看到任何回显,但从现在开始,您将不再处于以前的 Bash 中,您只是收到了一个新的非登录 bash shell。确认:

echo $0
Run Code Online (Sandbox Code Playgroud)

现在它不能再以破折号开头,即:

bash
Run Code Online (Sandbox Code Playgroud)

您可能想在哪里使用它?例如,当您需要通过将某些命令作为“参数”传递给 bash 来运行它们时。一个著名的例子是在命令后面使用“sudo”,该命令重定向到需要 sudo 权限的文件:

sudo echo 'example line' >> /path/to/file/requiring/sudo/privileges
Run Code Online (Sandbox Code Playgroud)

尽管 sudo 将以 root 身份运行该命令,但重定向仍然会提前执行(打开文件并准备重定向),因此会打印一个漂亮的“权限错误”,整个命令将失败。相反,您可以使用非登录 shell 作为 root,并将整个命令作为单个参数传递给它。例如:

sudo bash -c "echo 'example line' >> /path/to/file/requiring/sudo/privileges"
Run Code Online (Sandbox Code Playgroud)

当然,您不希望每次执行此类命令时都会触发 root 的启动文件!