使用Unix系统一段时间之后,应该对~/.bashrc
,~/.bash_profile
这些环境配置文件不陌生,而这些配置文件也是Unix Shell的精髓所在,在一次conda
的更新中,出现了一个不愉快的结果,使得我查阅了相关的资料,系统地了解了bash shell的环境配置过程,在此和大家分享一下。本文参考了网上很多博客的相关内容,向这些开源社区的斗士致敬。
1.shell的分类
1.1 交互式Shell和非交互式Shell
这个概念对于学过脚本语言的人来说,特别容易理解。以python为例,我们有两种方法玩python,第一种是直接打开终端,输入python
然后回车,就进入到交互式的python;第一种是创建一个xxx.py
文件,然后打开终端,输入python xxx.py
,这就是非交互式的python。
对于shell来说,交互模式就是你打开终端,shell等待你输入,并且一旦你回车即提交输入的命令,shell执行你的命令;非交互模式就是你写一个shell脚本,例如xxx.sh
,然后在终端执行这个脚本,这种情况,shell不与你进行交互,而是读取xxx.sh
中存储的命令,并且执行它们,当它读到文件结尾的EOF,shell就终止了。
变量的继承问题(存在问题,待更新)
交互式登陆开始于输入账户密码然后login,结束于终端logout或者直接关闭终端。新打开终端并不会记得上次关闭的终端里给变量赋的值,并且按⬆️也无法追溯上个关闭终端的历史输入命令。而非交互式终端就会继承,比如你先打开一个
1.1.1交互式登陆(不继承上一个Shell的变量)
(1)直接通过终端输入账号密码登录,会打印登陆信息
Last login: Tue Mar 12 10:38:46 on ttys000
You have mail.
zhangshengxingdeMacBook-Air:~ sxzhang$
(2)使用su - UserName
(注意有空格)
zhangshengxingdeMacBook-Air:~ sxzhang$ sh login1.sh
sxzhang@222.195.74.36's password:
Last login: Tue Mar 12 19:50:55 2019 from 114.214.194.133
[sxzhang@lw_s ~]$ su - yuantao
密码:
[yuantao@lw_s sxzhang]$ echo $-
himBH
[yuantao@lw_s sxzhang]$ su sxzhang
密码:
[sxzhang@lw_s ~]$ echo $-
hB
Note:两种登录方式看似一样,但是echo $-
的结果不一样,后面我们会提到,没有i的那种是非交互式的。
1.1.2非交互式登陆(会继承上一个shell的全部变量)
(1)su UserName
(2)执行脚本 (当我们执行脚本的时候.我们就已经进入到了一个子shell)
(3)交互式下,输入bash,进入一个子shell
1.2登录式Shell与非录陆式Shell
1.2.1登录式Shell
需要用户名、密码登录后才能进入的shell(或者通过--login”选项生成的shell,例如bash --login
)。
1.2.2非登录式shell:
不需要输入用户名和密码即可打开的Shell,例如:直接命令“bash”就是打开一个新的非登录shell。
1.2.3区别
(1)执行exit命令,退出一个shell(登录或非登录shell);执行logout命令,退出登录shell(不能退出非登录shell)
(2)对于用户来说,登陆shell和非登陆shell的主要区别是:启动shell时所执行的startup文件不同。登陆shell执行startup文件为:/etc/profile、~/.bash_profile、~/.bashrc(这里只是从现象上得出的推论,这种推论是有问题的,稍后讨论),而非登陆shell执行的startup文件仅为:~/.bashrc。
(3)区分方式是echo $0
,输出结果有短划线,是登录式:
Last login: Sun Mar 17 23:51:56 2019 from 114.214.252.93
[sxzhang@lw_s ~]$ echo $0
-bash
[sxzhang@lw_s ~]$ bash
[sxzhang@lw_s ~]$ echo $0
bash
2.配置文件
配置文件一般有两类六种,一种叫用户配置文件,放置在目录~/
下:
~/.bash_profile、~/.bashrc、和~/.bash_logout
一种叫全局配置文件,存放在目录/etc
下:
/etc/bashrc /etc/profile etc/logout
不同的Unix版本可能命名存在差别,但是关键字是一样的。~/.profile
(由BourneShell和Korn Shell使用)和~/.login
(由C Shell使用)两个文件是~/.bash_profile
的同义词,目的是为了兼容其它Shell。在Debian中就使用~/.profile
文件代 替~/.bash_profile
文件。
这些文件是隐藏文件,直接输入ls
指令是无法看到的,需要:
$ ls -ah /etc/
此外,并不是所有的电脑一开始都有这六种文件,可能只有其中几种,有必要的话,用户可以自己新建,但是名字必须符合规范。
2.1各种配置文件的作用时机
2.1.1用户配置文件
(1)~/.bash_profile
在用户每次开启一个新的登录式Shell时被读取,里面所有的命令都会被bash执行。如果已经登录了Shell,并在这个Shell里用Vim或者其他方式修改了~/.bash_profile
文件,这个时候,原有的Shell并不会立即执行这个新的文件,但是你可以:
source ~/.bash_profile
这样就相当于命令bash在此Shell重新执行一遍新文件里的所有命令。
当然你可以关掉旧的Shell,打开一个新的登录式Shell,这样新的~/.bash_profile
会被bash执行一遍。这个道理可以推广到其他物种配置文件。
(2)~/.bashrc
文件会在bash shell调用另一个bash shell时读取,也就是在shell中再键入bash命令启动一个新shell时就会去读该文件。例如,在shell里运行一个shell脚本,就相当于启动了一个新的shell,只不过这个shell在脚本运行完后就自动关闭了,于是,每当我们在shell里面运行一个shell脚本时,系统会先执行一遍~/.bashrc
,再执行shell脚本。这样可有效分离登录和子shell所需的环境。但一般来说都会在~/.bash_profile
里调用~/.bashrc
脚本以便统一配置用户环境,这可以通过在~/.bash_profile
添加如下命令实现:
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
#如果存在`~/.bashrc`文件就执行它
(3)~/.bash_logout
在退出shell时被读取。所以我们可把一些清理工作的命令放到这文件中。例如我们在一个shell里面运行一个shell脚本:
$ sh 1.sh
系统在运行完这个脚本后,会接着运行一次~/.bash_logout
。
2.1.2全局配置文件
那么,一个很自然的问题来了,全局配置文件什么时候发挥作用呢?
(1)登录式shell(包括交互式登录shell和使用“–login”选项的非交互shell),它会首先读取和执行/etc/profile
全局配置文件中的命令,然后依次查找~/.bash_profile
、~/.bash_login
和~/.profile
这三个配置文件,读取和执行这三个中的第一个存在且可读的文件中命令。除非被“–noprofile”选项禁止了。 (2)非登录式shell里,一遍读取 ~/.bashrc (和 /etc/bashrc )文件,不同的发行版里面可能有所不同,如RHEL6.3中非登录shell仅执行了“~/.bashrc”文件(没有执行/etc/bashrc),而KUbuntu10.04中却依次执行了/etc/bash.bashrc 和 ~/.bashrc 文件。
2.2各种Shell运行时,配置文件的执行顺序
(1)登录式(不管是不是交互)
- 登录:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile
- 开启子shell: ~/.bashrc--> /etc/bashrc
(2)交互式非登录
/etc/bashrc--> ~/.bashrc--> /etc/profile.d/*.sh
(3)非交互式非登录Shell
打开一个终端,输入bash
进入非登录式Shell,运行一个脚本,此时即非交互式非登录Shell,所以上面配置文件的都不会执行。但我们可以在脚本的最开始加上#!/bin/bash
,让脚本用登录式Shell来解释执行,此时,变成了登录非交互式Shell,参见(1)。
3.设计自己的配置文件
3.1先读一份配置文件
#未完待续
#取别名
alias cp='cp -i'
4.遇到的问题
更新Conda后,我发现每次打开终端,画风都是这样的:
(base) zhangshengxingdeMacBook-Air:~ sxzhang$
没错,最前面多了(bash)
,后来我发现是conda修改了我的~/.bash_profile
:
(base) zhangshengxingdeMacBook-Air:~ sxzhang$ cat .bash_profile
# added by Anaconda3 5.3.1 installer
#>>> conda init >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$(CONDA_REPORT_ERRORS=false '/Users/sxzhang/anaconda3/bin/conda' shell.bash hook 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/Users/sxzhang/anaconda3/etc/profile.d/conda.sh" ]; then
. "/Users/sxzhang/anaconda3/etc/profile.d/conda.sh"
CONDA_CHANGEPS1=false conda activate base
else
export PATH="/Users/sxzhang/anaconda3/bin:$PATH"
fi
fi
unset __conda_setup
#<<< conda init <<<
所以,每次打开终端,系统执行一遍~/.bash_profile
,会执行里面的:
conda activate base
我灵机一动,在这段话后面加了一句:
conda deactivate
于是,打开终端再也不会出现(base)
了。
另一个遇到的问题,为什么在Linux,我们总是配置~/.bashrc
但是在Mac下,我们配置就没有用呢,要去配置~/.bash_profile
才行?哈!什么情况加载~/.bashrc
,上面说得很清楚了,交互式非登录Shell,那在Mac下,你打开Term,echo一下$0,看看,前面是不是有个-号?说明这是交互式的登录Shell,当然不会加载~/.bashrc
了。实属正常。你肯定要问了,为啥Linux下没问题呢?你打开~/.profile
看看就知道了,这货竟然在~/.profile
文件里面source了~/.bashrc
!