@Shell 的启动方式及环境变量的配置

本文内容:Shell 的3钟启动方式;不同启动方式下环境变量的加载文件及其顺序;环境变量的持久化

Shell 是什么?

Shell 是指操作系统中,提供访问内核服务的程序,与之相对的是操作系统的内核(linux kernel)。--维基百科

我们平常使用的命令行界面的 Shell,只是众多的 Shell 的一种。通常,根据使用方式的不同 Shell 可以分成两类:命令行界面(CLI)和图形界面(GUI)。本文介绍的是命令行界面(CLI)。

命令行界面 Shell(以下简称 CLI-Shell),也是有很多种,通过运行cat /etc/shells命令,可以查看本机可以使用哪些 Shell。以下是 macOS 下可以使用的Shell:

# List of acceptable shells for chpass(1).

/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh

在 Linux 发行版中,默认的 Shell 就是 Bash,接下来的内容都会以 Bash 为例来进行说明。
在日常的工作中,经常会接触到环境变量、命令别名等概念。这些东西如何配置?如何生效?
这就涉及到 Shell 的启动脚本了,在介绍启动脚本前,先一起来认识下 Shell 的不同启动方式。

Shell 不同的启动方式

在你登入 Linux 系统启动一个 bash shell 时,默认情况下,bash 会在几个文件中查找命令,这些文件叫做启动文件环境文件。而,bash 检查的启动文件取决于你启动 bash shell 的方式。启动 bash shell 有三种方式:

  • 登录时作为默认登录 shell(login interactive shell)
  • 作为非登录shell 的交互式shell (non-login interactive shell)
  • 作为运行脚本的非交互式 shell(non-login non-interactive shell)

在具体说明各种启动方式前,先了解几个基本概念:

交互式 shell 和 非交互式 Shell ( interactive shell and non-interactive shell )

  • 交互式模式: 就是在终端上执行,shell等待你的输入,并且立即执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、退出。当你退出后,shell也终止了。
  • 非交互式模式: 以shell script(非交互)方式执行。在这种模式下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾EOF,shell也就终止了。

可以通过打印$-变量的值(代表当前 Shell 的选项标志),查看其中的i选项(表示 interactive shell)来区分交互式与非交互式 shell

zodas@localhost:~$ echo $-
himBH  #这是返回的内容
zodas@localhost:~$ ./tesh.sh echo $-
hb #这是返回的内容

登录 Shell 和非登录 Shell ( login shell and non-login shell )

  • 登录shell: 是需要用户名、密码登录后才能进入的shell(或者通过”–login”选项生成的shell)。
  • 非登录shell: 不需要输入用户名和密码即可打开的Shell,例如:直接命令“bash”就是打开一个新的非登录shell,在Gnome或KDE中(带图形界面的 linux 中)打开一个“终端”(terminal)窗口程序也是一个非登录shell。

执行exit命令,退出一个shell(登录或非登录shell);
执行logout命令,退出登录shell( 不能 退出非登录shell)。

zodas@localhost:~$ bash --login
zodas@localhost:~$ logout
zodas@localhost:~$ bash --login
zodas@localhost:~$ exit
logout

zodas@localhost:~$ bash
zodas@localhost:~$ logout
bash: logout: not login shell: use ‘exit’
zodas@localhost:~$ exit
exit

那么,如何判断一个已经打开的 Shell 是 login shell 还是 non-login shell?

A login shell is one whose first character of argument zero is ‘-’, or one invoked with the --login option. from bash reference manual

bash 是 login shell 时,其进程名为”-bash“ 而不是”bash”。 比如下面的命令行演示:

# 在 login shell 中:
[zodas@localhost ~]$ echo $0
-bash
[zodas@localhost ~]$ ps -ef | grep '\-bash' | grep -v grep
root     16823 16821  0 May06 pts/0    00:00:00 -bash
zodas     21135 21134  0 May07 pts/1    00:00:00 -bash
 
#在一个非登陆shell中:
zodas@localhost:~$ echo $0
/bin/bash
zodas@localhost:~$ ps -ef | grep '\-bash' | grep -v grep
zodas@localhost:~$ 

登录 shell(login interactive shell)

当你登录Linux系统时,bash shell 会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令:

/etc/profile            <==主启动文件,系统上每个用户登录时都会执行该启动文件;另外,该文件还配置了对于/etc/profile.d 目录下的所有文件也会在用户登录时被启动
/$HOME/.bash_profile    
/$HOME/.bashrc
/$HOME/.bash_login
/$HOME/.profile

另外,当你 cat /etc/profile 时,会发现该文件中包含下面这段脚本,它用来迭代/etc/profile.d目录下的所有文件,一般,我们会将特定应用程序的启动文件放置在该目录下,当用户登录时,shell 会执行这些文件。

for i in /etc/profile.d/*.sh ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then
            . "$i"
        else
            . "$i" >/dev/null
        fi
    fi
done

TIPS:对全局环境变量来说(Linux系统中所有用户都需要使用的变量),可能更倾向于将新的或修改过的变量设置放在/etc/profile文件中,但这可不是什么好主意。如果你升级了所用的发行版,这个文件也会跟着更新,那你所有定制过的变量设置可就都没有了。
最好是在/etc/profile.d目录中创建一个以.sh结尾的文件。把所有新的或修改过的全局环境变量设置放在这个文件中。
在大多数发行版中,存储个人用户永久性 bash shell 变量的地方是 $HOME/.bashrc 文件。

交互式 Shell 进程(non-login interactive shell)

若你的 bash shell 不是登录时启动的,如通过在命令行提示符下乔茹 bash 命令时启动,那么,此时启动的就是非登录下的交互式 shell。以这种方式启动的 shell,其启动文件为:

/$HOME/.bashrc

即该不会访问/etc/profile文件,只会检查用户目录下的 .bashrc文件。
另外,当你 cat .bashrc文件后,会发现该文件中包含下面这段脚本,即该启动方式登录的 shell 还会加载 /etc/bashrc 文件。

if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

非交互式 Shell(non-login non-interactive shell)

系统执行 shell 脚本时使用的就是这种shell。对于这种启动方式,bash shell提供了BASH_ENV环境变量。当shell启动一个非交互式shell进程时,它会检查这个环境变量来查看要执行的启动文件。如果有指定的文件,shell会执行该文件里的命令,这通常包括shell脚本变量设置。

那如果BASH_ENV变量没有设置,shell脚本到哪里去获得它们的环境变量呢?

对于启动子shell的脚本,如果父 shell 是登录 shell,在/etc/profile/etc/profile.d/*.sh$HOME/.bashrc文件中设置并导出了变量,用于执行脚本的子shell就能够继承这些变量。
对于那些不启动子shell的脚本,变量已经存在于当前shell中了。所以就算没有设置
BASH_ENV,也可以使用当前shell的局部变量和全局变量。

环境变量持久化

如何设置局部用户定义变量?

[zodas@localhost ~]$ my_variable="hello world" <==定义用户变量,注意格式。
[zodas@localhost ~]$ echo $my_variable
hello world
[zodas@localhost ~]$ bash <==启动一个子 shell
[zodas@localhost ~]$ echo $my_variable <== 发现输出的是空行

[zodas@localhost ~]$ exit   

tips: 所有的环境变量名均使用大写字母,这是bash shell的标准惯例。如果是你自己创建的局 部变量或是shell脚本,请使用小写字母。变量名区分大小写。在涉及用户定义的局部变量 时坚持使用小写字母,这能够避免重新定义系统环境变量可能带来的灾难。

如何设置全局环境变量?
从上面的例子可以看到,可以通过等号该变量赋值,那么该变量就是局部用户定义变量,但若启动子 shell,子 shell 并不能继承父 shell 刚创建的局部用户定义变量,如上例,输出的是空行。那么,若要让子 shell 也能拿到父 shell 刚创建的用户变量,则需要将其变为全局环境变量。

[zodas@localhost ~]$ my_variable="hello world"      <==首先创建局部用户变量
[zodas@localhost ~]$ export my_variable             <==然后导出到全局环境中
[zodas@localhost ~]$ bash               <==启动子 shell
[zodas@localhost ~]$ echo my_varialbe   <==发现子 shell 已继承了在父 shell 中定义的用户变量
hello world
[zodas@localhost ~]$ exit

如何将环境变量永久化?
分析 shell 的三种启动方式后,我们发现比较关键的是几个启动文件为:

/etc/profile        <==该文件会被加载,是因为在 /etc/profile 中有相应的脚本
/etc/profile.d/*.sh
/$HOME/.bashrc
/etc/bashrc         <==该文件会被加载,是因为在 .bashrc 中有相应的脚本

所以,
对于系统环境变量,在 /etc/profile 中定义用户变量,并导出为全局环境变量,则该变量便会被持久化了。(注:由于系统更新时,该文件会被覆盖,导致配置过的变量丢失,所以,建议在 /etc/profile.d目录下添加响应的 .sh脚本文件的形式,添加系统环境变量,而不是以修改/etc/profile文件的形式添加系统环境变量。)

对于用户环境变量,在 /$HOME/.bashrc中定义用户变量,并导出为全局环境变量,则该变量便会被持久化了。
当然,以上只是一些最简单的持久化方法。关键在于理解每个启动文件的加载时机,并将变量添加到合适的启动文件中,便能达到自己想要的效果。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343