在我的 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
- 没有
i
in$-
表示 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
)。
为什么scp
会sftp
失败
SCP(安全副本)和SFTP(安全文件传输协议)本地和远程端有自己的协议来交换有关正在传输的文件的信息。来自远程端的任何意外文本都会被(错误地)解释为协议的一部分,并且传输失败。根据一个蜗牛书常见问题解答
然而,经常发生的情况是,服务器上的系统或每用户 shell 启动文件中存在一些语句(
.bashrc
、.profile
、/etc/csh.cshrc
、.login
等),它们在登录时输出文本消息,供人类阅读(例如fortune
、echo "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
更安全并且使事情变得简单。