结论 / TLDR

结论 / TLDR

在我的 Arch 安装中,/etc/bash.bashrc包含/etc/skel/.bashrc以下几行:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

在 Debian 上,/etc/bash.bashrc有:

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

/etc/skel/.bashrc

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

然而,根据man bash,非交互式 shell 甚至不读取这些文件:

例如,当bash以非交互方式启动时,要运行 shell 脚本,它会在环境中查找变量BASH_ENV,如果变量出现在环境中,则扩展其值,并使用扩展后的值作为要读取和执行的文件的名称。 Bash 的行为就像执行了以下命令:if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi但变量的值PATH不用于搜索文件名。

如果我理解正确的话,只有设置为指向这些*.bashrc文件时才会读取这些文件。BASH_ENV这是不可能偶然发生的事情,只有当有人明确相应地设置变量时才会发生。

.bashrc这似乎打破了通过设置自动获取用户源的脚本的可能性BASH_ENV,这可能会派上用场。鉴于 bash 在非交互运行时永远不会读取这些文件,除非明确告知这样做,为什么默认*bashrc文件不允许这样做?

答案1

这是几周前我打算在这里发布的一个问题。喜欢特登,我知道 a.bashrc仅源自交互式 Bash shell,因此不需要.bashrc检查它是否在交互式 shell 中运行。令人困惑的是,全部我使用的发行版(Ubuntu、RHEL 和 Cygwin)进行了某种类型的检查(测试$-$PS1)以确保当前 shell 是交互式的。我不喜欢货物崇拜编程所以我开始理解我的.bashrc.

Bash 有一个远程 shell 的特殊情况

研究这个问题后我发现远程 shell受到不同的对待。虽然非交互式 Bash shell 通常不会~/.bashrc在启动时运行命令,但当 shell 启动时会出现一种特殊情况。由远程 shell 守护进程调用:

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

例子

在遥控器的开头插入以下内容.bashrc。 (如果.bashrc源自.profile.bash_profile,则在测试时暂时禁用此功能):

echo bashrc
fun()
{
    echo functions work
}

在本地运行以下命令:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • 没有iin$-表示 shell 是非交互式
  • 没有引入-表明$0该 shell 不是登录外壳

远程定义的 Shell 函数.bashrc也可以运行:

$ ssh remote_host fun
bashrc
functions work

我注意到~/.bashrc仅有的当命令被指定为 的参数时获取ssh。这是有道理的:whenssh用于启动常规登录 shell,.profile.bash_profile运行(并且.bashrc仅在这些文件之一明确执行此操作时才获取源)。

我看到.bashrc在运行(非交互式)远程命令时使用 source 的主要好处是可以运行 shell 函数。然而,典型中的大多数命令.bashrc仅在交互式 shell 中相关,例如,除非 shell 是交互式的,否则不会扩展别名。

远程文件传输可能会失败

rsh当或ssh用于启动交互式登录 shell 或使用非交互式 shell 运行命令时,这通常不是问题。然而,它可能是个问题对于诸如rcp和使用的scp程序sftp远程 shell用于传输数据。

事实证明,在使用该scp命令时,远程用户的默认 shell(如 Bash)会隐式启动。手册页中没有提及这一点——仅提及了其数据传输的scp用途。ssh这导致的后果是如果.bashrc包含任何打印到标准输出的命令,文件传输将失败,例如, scp 失败且没有错误

另请参阅 15 年前的相关 Red Hat 错误报告,当 /etc/bashrc 中有 echo 命令时 scp 中断(最终被关闭为WONTFIX)。

为什么scpsftp失败

SCP(安全副本)SFTP(安全文件传输协议)本地和远程端有自己的协议来交换有关正在传输的文件的信息。来自远程端的任何意外文本都会被(错误地)解释为协议的一部分,并且传输失败。根据一个蜗牛书常见问题解答

然而,经常发生的情况是,服务器上的系统或每用户 shell 启动文件中存在一些语句(.bashrc.profile/etc/csh.cshrc.login等),它们在登录时输出文本消息,供人类阅读(例如fortuneecho "Hi there!"、 ETC。)。

当附加到标准输入时,此类代码应该仅在交互式登录时生成输出 tty。如果它不进行此测试,它将在不属于的地方插入这些文本消息:在这种情况下,会污染scp2/sftp和之间的协议流sftp-server

shell 启动文件之所以相关,是因为sshd 代表用户启动任何程序时使用用户的 shell (使用例如/bin/sh -c“命令”)。这是 Unix 的传统,并且有以下优点:

  • 用户的常用设置(命令别名、环境变量、umask 等)在远程命令运行时生效。
  • 如果身份验证由于某种原因仍然意外成功,将帐户的 shell 设置为 /bin/false 来禁用它的常见做法将阻止所有者运行任何命令。

SCP协议详细信息

对于那些对 SCP 工作细节感兴趣的人,我在以下位置找到了有趣的信息:SCP协议如何工作其中包括详细信息在远程端使用会话 shell 配置文件运行 scp 吗?:

例如,如果您将其添加到远程系统上的 shell 配置文件中,则可能会发生这种情况:

回声“”

为什么它只是挂起?这scp来自于如何来源模式等待第一个协议消息的确认。如果它不是二进制 0,则它认为这是远程问题的通知,并等待更多字符形成错误消息,直到新行到达。由于您在第一行之后没有打印另一行,因此您的本地文件scp只是停留在循环中,被阻塞在read(2).同时,在远程端处理完 shell 配置文件后,scp启动了接收器模式,该模式也阻塞在 上read(2),等待表示数据传输开始的二进制零。

结论 / TLDR

典型中的大多数语句.bashrc仅对交互式 shell 有用 – 在使用rsh或运行远程命令时不起作用ssh。在大多数此类情况下,不需要设置 shell 变量、别名和定义函数 - 并且打印任何文本scp如果使用或等程序传输文件,则标准输出会非常有害sftp。验证当前 shell 是非交互式后退出是.bashrc.

答案2

手册页忽略了提及非交互式远程 shell 的bash来源.bashrc,如

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1010

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1050

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);

答案3

按照惯例,.bashrc这是用户存储 shell 自定义配置的位置。

这些自定义配置可以是环境变量、别名、花式提示符。对于非交互式 shell,那些缺少的东西是没有意义的。此外,非交互式 shell 可以在许多上下文中调用,您不确定这些环境变量是否会导致漏报,甚至安全漏洞。

最接近的示例是别名,例如:

alias cp='cp -i'

然后它会永远挂起您的非交互式外壳。

因此检查在顶部执行.bashrc以确保我们不会遇到麻烦。


因为shell可以称为非交互式登录shell,因此明确阻止来源*bashrc是没有意义的。

当外壳是称为非交互式登录 shell,它源,然后源在、、 和/etc/profile中找到的第一个:~/.bash_profile~/.bash_login~/.profile

当 bash 作为交互式登录 shell 被调用时,或者作为带有 --login 选项的非交互式 shell,它首先从文件 /etc/profile 中读取并执行命令(如果该文件存在)。读取该文件后,它会按顺序查找 ~/.bash_profile、~/.bash_login 和 ~/.profile,并从第一个存在且可读的文件中读取并执行命令。

没有什么可以阻止这些文件.bashrc自行获取,因此在内部进行检查.bashrc更安全并且使事情变得简单。

相关内容