为什么bashrc检查当前shell是否是交互式的?


62

在我的拱安装,/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甚至不会读取以下文件:

   When  bash  is  started  non-interactively,  to run a shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file
   name.

如果我理解正确,*.bashrc则仅在BASH_ENV设置为指向文件时才读取文件。这是偶然发生的,只有在有人明确设置了相应变量后才会发生。

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


5
有关实际的答案,请参阅SCP毫无错误地失败
Gilles 2016年

Answers:


69

这个问题是我几周前要在这里发布的。像terdon一样,我知道a .bashrc仅来自交互式Bash shell,因此无需.bashrc检查它是否在交互式shell中运行。令人困惑的是,我使用的所有发行版(Ubuntu,RHEL和Cygwin)都进行了某种类型的检查(测试$-$PS1),以确保当前shell是可交互的。我不喜欢崇拜货物的程序,所以我开始着手了解该代码的目的.bashrc

Bash对于远程shell有特殊情况

在研究了该问题之后,我发现对远程Shell的处理有所不同。虽然非交互式Bash Shell通常~/.bashrc在启动时不运行命令,但是当远程远程守护程序调用 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
  • No iin $-表示外壳是非交互式的
  • 没有领导-$0表示该外壳不是登录shell

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

$ ssh remote_host fun
bashrc
functions work

我注意到,~/.bashrc只有当命令被指定为参数来源ssh。这是有道理的:when ssh用于启动常规登录Shell .profile.bash_profile运行(并且.bashrc仅在其中一个文件明确这样做的情况下才提供source)。

我可以看到.bashrc运行(非交互式)远程命令时获得源代码的主要好处是可以运行shell函数。但是,典型命令中的大多数命令.bashrc仅在交互式外壳中才有用,例如,除非外壳是交互式的,否则别名不会扩展。

远程文件传输可能会失败

当使用rshssh用于启动交互式登录Shell或使用非交互式Shell运行命令时,通常这不是问题。然而,这可能是个问题了,如程序rcpscpsftp使用远程shell传输数据。

事实证明,使用该scp命令时,隐式启动了远程用户的默认外壳程序(如Bash)。有没有在手册页没有提到这-只提及scp使用ssh它的数据传输。这样的结果是,如果.bashrc包含任何可打印到标准输出的命令,文件传输将失败,例如, scp失败而没有error

另请参阅此15年前的相关Red Hat错误报告,当/ etc / bashrc中有一个echo命令(最终以形式关闭WONTFIXscp中断

为什么scpsftp失败

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

经常会发生什么,不过,是有系统无论是陈述或者每个用户的shell启动文件的服务器上(.bashrc.profile/etc/csh.cshrc.login,等),由人阅读登录输出文本消息,意(如fortuneecho "Hi there!",等等。)。

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

外壳程序启动文件完全相关的原因是,sshd 在代表用户启动任何程序时 (例如,使用/ bin / sh -c“ command”),都使用了用户外壳程序。这是Unix的传统,具有以下优点:

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

SCP协议详细信息

对于那些对SCP的工作原理感兴趣的人,我在SCP协议的工作原理中发现了有趣的信息,其中包括有关在远端使用健谈的shell配置文件运行scp的详细信息

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

回声“”

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

结论/ TLDR

典型的大多数语句.bashrc仅对交互式shell有用,而在使用rsh或运行远程命令时则无效ssh。在大多数情况下,不需要设置shell变量,别名和定义函数- 如果使用诸如或的程序传输文件,将任何文本打印为标准输出都是有害的。验证当前外壳程序是非交互式后退出是最安全的行为。scpsftp.bashrc


好文章。但是我应该这样做吗?我确实已经通过.bashrc签入,但仍然用0代码退出了scp,并且不将任何内容复制到远程邮箱
ses

@ses最好提出一个新问题,在这里您可以更详细地描述您的情况。
Anthony Geoghegan

16

手册页忽略了提及非交互式远程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);

您是否从.bash_profile(或.profile)中获取.bashrc?
格伦

是的,但这不是重点。空.bash_profile将显示相同的行为。
Mikel

不知道这在显示什么。您是在说rsh(我从未使用过)来源~/.bashrc吗?而且,通过扩展,Linux发行版将其阻止,bash以防万一有人试图使用rsh?运行脚本。ssh server请勿触摸,.bashrc因为无论如何这都是一个登录外壳。除非您的系统是从中获取〜/ .bashrc`的系统之一~/.profile。而且ssh server command甚至不应该这样做。当然不在我的系统上。
terdon

2
不。我将您链接到bash源,而不是rsh源。:)评论也适用ssh,它是很久以前写的。查看更多源的更新答案。
Mikel

嗯,这很有帮助,谢谢。我将如何测试呢?我尝试添加一个echo在最顶部/etc/bash.bashrc~/.bashrc(交互式shell测试之前),然后ssh到目标机器ssh server hostname,虽然hostname是跑,我没有看到任何我的回声。是我的shell_level(无论是什么)不是<2吗?
terdon

5

按照惯例,.bashrc是用户存储外壳的自定义配置的地方。

这些自定义配置可以是环境变量,别名,特殊提示。对于非交互式外壳,缺少这些内容毫无意义。而且,可以在许多上下文中调用非交互式外壳,您不确定这些环境变量是否会导致错误的否定情况,甚至导致安全漏洞。

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

alias cp='cp -i'

然后它将永久挂起您的非交互式外壳。

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


由于该外壳程序可以称为非交互式登录外壳程序,因此显式的块采购*bashrc毫无意义。

当炮弹被称为非交互式登录shell,它源/etc/profile,那么其中的第一个发现~/.bash_profile~/.bash_login~/.profile

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

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


对,我知道。这就是为什么非交互式Shell根本不提供*bashrc文件的原因。我的问题是,如果非交互式shell仍然无法读取这些文件,为什么各种Linux供应商会遇到显式阻止非交互式shell读取这些文件的麻烦。
terdon

1
@terdon:因为可以将外壳称为非交互式登录外壳,所以登录外壳将是source .bash_profile,可以是source .bashrc
cuonglm '16

不,在非交互式shell中,唯一要读取的是$BASH_ENV。双方*profile*bashrc会被忽略。或者,至少,这就是手册页所说的。但是,正如Mikel所示,手册页可能在说谎。
terdon

@terdon:阅读我的最新答案,其中包含链接。您是否尝试bash -l -c :调用非交互式登录外壳?
cuonglm '16

哇。你是绝对正确的。我已经阅读了大约--login100遍,但是显然它从未注册过。谢谢!
terdon
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.