为什么默认情况下OSX登录Shell上是交互式Shell?


43

在Linux和据我所知的所有Unix系统中,终端仿真器默认运行交互式非登录外壳。这意味着对于bash,启动的shell将:

当一个交互的shell但不是登录shell启动时,bash读取并执行命令/etc/bash.bashrc~/.bashrc,如果这些文件存在。使用--norc 选项可以禁止这种情况 。

--rcfile 文件选项将强制bash读取和文件,而不是执行命令/etc/bash.bashrc~/.bashrc

对于登录shell:

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

--noprofile启动外壳程序以禁止此行为时,可以使用该选项。

但是,在OSX上,从默认终端(Terminal.app)中启动的默认外壳程序(即bash)实际上是源代码~/.bash_profile~.profile其他内容。换句话说,它的作用类似于登录外壳程序。

主要问题:为什么默认的交互式shell是OSX上的登录shell?为什么OSX选择这样做?这意味着~/.bashrc在OSX上所有基于shell的东西的说明/教程中提到的更改内容都将失败,反之亦然~/.profile。尽管如此,尽管可以对苹果提出许多指控,但雇用无能或愚蠢的开发人员并不是其中之一。大概他们对此有充分的理由,那为什么呢?

子问题:Terminal.app是否实际上运行交互式登录外壳,或者它们是否改变了bash的行为?这是特定于Terminal.app还是与终端模拟器无关?


2
Terminal.app运行登录外壳程序。我不知道为什么苹果选择这么做。
吉尔(Gilles)'“ SO-别再邪恶了”

Answers:


33

它就是这样认为的工作是,在点当你得到一个shell提示符下,双方.profile.bashrc已运行。达到这一点的具体细节是次要的,但是如果两个文件都根本没有运行,那么您的外壳将具有不完整的设置。

Linux(和其他基于X的系统)上的终端模拟器不需要自己运行的原因.profile是,当您登录到X时,它通常已经运行了。其中的设置.profile应该是可以由子进程继承,因此只要您在登录时(例如通过.Xsession)执行一次,其他任何子shell都不需要重新运行它。

正如由Alan Shutko链接的Debian Wiki页面所解释的那样:

“为什么.bashrc要从中分离一个文件.bash_profile?这是出于历史原因而完成的,这是因为与今天的工作站相比,计算机的运行速度非常慢。在.profile.bash_profile可能要花很长时间的情况下处理命令,尤其是在工作量很大的计算机上必须由外部命令(bash预先完成)来完成,因此放置了困难的初始设置命令,这些命令创建了可以传递给子进程的环境变量,并放置了.bash_profile不继承的瞬态设置和别名。在.bashrc这样他们可以通过每个子shell重读“。

除一件事外,所有相同的规则也适用于OSX- .profile登录时OSX GUI不会运行,这显然是因为它具有加载全局设置的方法。但这意味着OSX 的终端仿真器确实需要运行.profile(通过告诉外壳程序启动它是登录外壳程序),否则最终可能会损坏外壳程序。


现在,bash的一种愚蠢的特性,不是大多数其他shell都共享,.bashrc它是作为登录shell启动时不会自动运行的。对此的标准解决方法是在中包含类似以下命令的内容.bash_profile

[[ -e ~/.profile ]] && source ~/.profile    # load generic profile settings
[[ -e ~/.bashrc  ]] && source ~/.bashrc     # load aliases etc.

另外,也可能根本没有.bash_profile,仅在通用.profile文件中包含一些特定于bash的代码,以便.bashrc在需要时运行。

如果OSX默认.bash_profile.profile 不这样做,那可以说是一个错误。无论如何,正确的解决方法是将这些行添加到中.bash_profile


编辑: 正如strugee所述,OSX上的默认shell以前是tcsh,在这方面其行为要聪明得多:当作为交互式登录shell运行时,tcsh会自动读取.profile .tcshrc / .cshrc,因此不需要任何.bash_profile技巧(如技巧)如上所示。

基于此,我有99%的肯定OSX无法提供适当的默认值.bash_profile是因为,当他们从tcsh切换到bash时,Apple的人们根本没有注意到bash的启动行为中的这种小缺陷。使用tcsh,不需要这些技巧-从OSX终端仿真器Just Plain Works将tcsh作为登录shell启动,并且在没有这种麻烦的情况下做正确的事情。


1
谢谢,我有点知道。我的问题是,为什么 OSX选择以.bashrc不相关的方式进行设置?他们为什么选择让所有shell登录shell?据我所知,只有您的最后一句话可以解决这个问题,也只能说这是一个错误。
terdon

1
不,问题在于它们在其终端仿真器中启动登录Shell。点文件是默认的bash行为,启动登录shell将读取配置文件等,而不是bashrc等。问题是为什么运行登录shell而不是非登录shell。
terdon

2
是的,我和艾伦都解释了这是因为,与Linux不同,.profile当用户登录GUI时OSX不会执行,因此他们必须稍后执行它才能获取环境变量(如$PATH通常在.profile,中正确设置的) 。作为副作用,这导致.bashrc没有来源的事实是一个错误;您可以争论这是bash还是OSX中的错误,但这并不能改变正确的行为是确保同时加载from .profile和bash配置设置这两个事实.bashrc
Ilmari Karonen 2014年

1
拥有.bashrcsource .profile并不是一个好主意,因为它将导致每个subshel​​l重新运行.profile。如果没有其他问题,这将导致常见的.profile习惯用法,例如export PATH = "$HOME/bin:$PATH"在之前保留多余的条目$PATH。拥有.profile源代码.bashrc更有意义,但是只有在检查了它所运行的shell实际上是bash之后,才可以使用。其.bash_profile源既.profile .bashrc,正如我上面所说,是,国际海事组织,最明智的选择。
Ilmari Karonen 2014年

2
我是说“为什么他们使用登录Shell?”的答案。是“因为他们需要负担.profile”,而回答“他们为什么不源.bashrc来自.profile呢?” 是“因为它是错误!” 严重的是,这不可能是一个故意的决定;这只是他们忽略的事情,大概是因为在Mac生态系统中,shell是二等公民,大多数用户不应该使用。(注:另请参阅strugee的答案以作历史解释;这几乎可以肯定是从tcsh到bash的回归。)
Ilmari Karonen 2014年

14

X终端应用程序默认运行非登录Shell的主要原因是,在开始时,您的.Xsession将运行.profile来设置初始登录项。然后,由于已经完成所有设置,因此终端应用程序不需要运行它,因此可以运行.bashrc。在https://wiki.debian.org/DotFiles上讨论为何如此重要:

让我们以xdm为例。皮埃尔有一天从假期回来,发现他的系统管理员已在Debian系统上安装了xdm。他登录正常,xdm读取他的.xsession文件并运行fluxbox。一切似乎都还不错,直到他在错误的语言环境中收到错误消息为止!由于他覆盖了.bash_profile中的LANG变量,并且由于xdm从不读取.bash_profile,因此现在将其LANG变量设置为en_US而不是fr_CA。

现在,这个问题的简单解决方案是他可以将窗口管理器配置为启动“ xterm -ls”,而不是启动“ xterm”。该标志告诉xterm,它应该启动登录shell,而不是启动普通的shell。在这种设置下,xterm生成/ bin / bash,但是将“-/ bin / bash”(或者也许是“ -bash”)放在参数向量中,因此bash的作用类似于登录shell。这意味着,每当他打开一个新的xterm时,它将读取/ etc / profile和.bash_profile(内置的bash行为),然后读取.bashrc(因为.bash_profile表示这样做)。乍一看这似乎效果很好-他的点文件并不繁重,所以他甚至都没有注意到延迟-但还有一个更细微的问题。他还直接从其fluxbox菜单启动了网络浏览器,并且Web浏览器从fluxbox继承了LANG变量,该变量现在被设置为错误的语言环境。因此,尽管他的xterm可能很好,并且从他的xterm启动的任何东西都可能很好,但是他的Web浏览器仍然在错误的语言环境中提供页面。

在OS X上,用户环境不是由一堆shell脚本启动的,并且启动后任何时候都不会源.profile。(这太可惜了,因为这意味着设置环境变量会更加烦人,但这就是生命。)既然没有,它何时会运行.profile? 只有当你进去吗?这似乎毫无意义,因为许多工具永远不会成为ssh的目标。还可以使终端默认运行登录外壳程序,以便.profile可以在某个时间运行。

那么.bashrc呢?它没用吗?否。它仍然具有VT100时代的目的。除了打开“终端”窗口外,它可用于任何时候打开外壳的时间。因此,如果您退出Emacs或vi,或者您使用su用户。


1
您引用的Debian页面解释了为什么Debian的.profile源代码.bashrc,而不是为什么OSX决定默认使所有shell登录shell的原因。实际上,您正在回答我的另一个问题,谢谢!但是,您的回答并不能解释OSX的选择,就我所知,Debian报价在这里是完全无关的(请让我知道我是否遗漏了要点)。OSX方式使.bashrcetc无效,并且不再.profile有用。
terdon

4

我不知道他们为什么会那样做。但是,这是我的猜测。

首先,值得注意的是,在GNU / Linux系统上,您当然可以切换到vt1,vt2等。在那里您可以获得登录shell。在OS X系统上,没有等效功能。访问UNIX基础的唯一方法是通过终端仿真器或通过单用户模式(免责声明:我从未真正使用过单用户模式;它是命令行驱动的IIRC,但我可能错了)。因此,在OS X中,仿真器中的默认值是整个系统的默认值。

现在,为什么要将默认的登录shell设置为?我可以想到有两个原因(阅读:不多)。

  • 如果您通过SSH进入设备箱,它将提供一致的用户体验。(对于OS X的服务器版本特别重要-如果正在运行OS X服务器,则可能是新手。)
  • OS X的默认shell曾经是tcsh。这大概是您的猜测,但这可能是tcsh在作为登录shell运行时通常做了一些事情,并且历史模式被卡住了。(不过,我对此表示怀疑-也许一位年长的常客会告诉我们。)
  • “我们是Apple。我们是地球上最大的UNIX发行版的供应商。我们的理由多么微不足道;如果我们做出决定,则您的工具必​​须处理它。”

老实说,我已经使用达尔文大约6年了,我无法正确回答这个问题。这对我来说也没有任何意义。

要回答您的子问题,bash没有打补丁或任何东西(至少为此)。默认情况下,默认的终端模拟器运行登录外壳程序,大概是iTerm复制该外壳程序。


1
提及tcsh +1,因为在这方面它的行为比bash的行为更聪明:当tcsh作为交互式登录shell启动时,tcsh 既可以 获取源代码.profile.tcshrc/或源代码,.cshrc也不需要像我在回答中给出的那样。鉴于此,我怀疑此行为是由tcsh切换到bash引起的不固定的回归。
Ilmari Karonen 2014年

@IlmariKaronen您是说(这里和那里)tcsh源.login.tcshrc/ .cshrc吗?tcsh来源毫无意义.profile; 它通常包含不接受sh语法的命令tcsh。我没有macOS,但我/bin/tcsh在Ubuntu 16.04上创建了登录shell。.profile不是来源。tcsh(1)没有提及该文件,也没有提及tcsh(1)
伊莱亚·卡根

3

这是当前状态的更新:随着新MacOSX版本的发布和登录行为的更改,答案变得过时。

在2014年提出并回答了这个问题。我一直在研究该主题,以尝试构建一个通用的.bashrc和.bash_profile集,以便在不同的Linux发行版和BSD中使用(如果不是发行版,则称为它们)。

我最近购买了安装了Sierra的二手Mac Mini,所以现在我的系统具有10.6、10.10、10.11和10.12。尽管我已经替换了较旧的版本(为它们以前的状态留下了线索),但仍未修改10.12 Sierra安装。

结果:在10.12 Sierra中,没有创建默认的.bashrc,.bash_profile或.profile。在/ etc中,有bashrc,bashrc_Apple_Terminal和配置文件。/ etc / profile的内容:

# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi

/ etc / bashrc的内容:

# System-wide .bashrc file for interactive bash(1) shells.
if [ -z "$PS1" ]; then
   return
fi

PS1='\h:\W \u\$ '
# Make bash check its window size after a process completes
shopt -s checkwinsize

[ -r "/etc/bashrc_$TERM_PROGRAM" ] && . "/etc/bashrc_$TERM_PROGRAM"

/ etc / bashrc_Apple_Terminal脚本设置PROMPT_COMMAND变量,并设置一种机制来保留每个终端的会话状态。如果退出终端应用程序,则当再次启动该应用程序时,将恢复先前会话的状态。否则,/ etc / bashrc中几乎没有设置任何内容(最低$ PS1),而〜/ .bashrc和.bash_profile(或.profile中)的$ PATH设置中却没有设置任何其他自定义项(实际的$ PS1,别名,函数)。 ,由.bash_profile来源)。

我已经确认iTerm2的行为与终端应用程序相同,只是启动登录会话的默认行为是可见的并且可以编辑。

如果您运行X11(现在为XQuartz)XTerm终端会话,则将看到非登录会话,与Linux系统上的会话相同。.bash_profile(或.profile)被跳过,您只得到.bashrc。

而且,这是我的答案:

尽管终端应用程序声称是xterm(TERM = xterm-256color),但除非安装了X11,否则它不会设置$ DISPLAY变量。我们看到的是X环境的模拟,但不是完全真实的模拟。如果使用-X开关SSH进入另一个系统(启用X11转发),它将失败,因为没有$ DISPLAY变量。如果已安装X11,然后将SSH插入另一个系统,则X11转发将成功。

底线(和简短答案):终端应用不是真正的X11终端(未设置$ DISPLAY);它的行为更像是SSH登录,而不是XTerm会话,因为它必须是登录会话才能从/ etc / profile和〜/ .bash_profile或〜/ .profile设置值


0

以上这些问题的答案解释了为什么交互shell默认情况下在MacOS登录shell的原因:设置在/etc/profile~/.profile通过基于X的系统上以非登录shell继承,但不能在MacOS。在这里我想提醒您,由于存在以下原因,您应该始终在macOS上使用登录shellpath_helper

 cat /etc/profile # or cat /etc/zprofile
# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi

path_helper实用程序可以读取文件的内容目录/etc/paths.d/etc/manpaths.d 并追加其内容的PATHMANPATH分别的环境变量。(MANPATH除非已在环境中设置环境变量,否则将不会对其进行修改。)

如果您使用的是非登录外壳,则不会导入某些路径。

我认为对于开发人员而言,将文件放入其中/etc/paths.d以导入其路径值始终是一个好主意,但不要将可调用二进制文件符号链接到诸如/usr/bin或的位置/bin。(默认PATH值为/usr/bin:/bin:/usr/sbin:/sbin。并非每个人都使用Homebrew或MacPorts向其中添加自定义值PATH。因此,并非总是安全的位置放置可调用命令的符号链接。)

这是中的文件示例/etc/paths.d。基本上,您在每一行都输入一个值。

 cat /etc/paths.d/Wireshark
/Applications/Wireshark.app/Contents/MacOS

参考:

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.