Linux 环境变量能提升 shell 使用体验
很多程序和脚本都通过环境变量获取系统信息、存储临时数据和配置信息
更多精彩
- 更多技术博客,请移步 IT人才终生实训与职业进阶平台 - 实训在线
6.1 什么是环境变量
- shell 中用来存储有关 shell 会话和工作环境的变量,被叫做 环境变量( Environment Variable )
- 环境变量 被存储在内存中,方便程序或在 shell 中运行的脚本能轻松访问
- shell 中的环境变量分为两类
- 全局环境变量
- 局部环境变量
6.1.1 全局环境变量
-
全局环境变量 之所以被叫做 全局 ,是因为其对于 父 shell 和所有生成的 子 shell 会话都是可见的
- 但 局部环境变量 只对创建它们的 shell 可见
- Linux 内置了很多默认的 全局环境变量 ,会在用户登录 shell 时依次加载,这种 全局环境变量 被叫做 系统环境变量
- 系统环境变量 基本上都是使用全大写字母,用来区别用户自行创建的环境变量
6.1.1.1 env 命令和 printenv 命令查看全局环境变量
- 使用
env
命令和printenv
命令可以查看当前系统的全局环境变量,如下图
-
printenv
命令和env
命令的区别在于,printenv
命令可以查看单个变量,而env
命令不行,如下图
6.1.1.2 echo 命令查看全局环境变量
- 使用
echo
命令也可以查看单个变量的值,但语法稍微复杂一点,需要在变量名称前加一个美元符号 ,如下图
6.1.1.3 全局环境变量的全局观
- 前面说过,全局环境变量 之所以被叫做 全局 ,是因为在 父 shell 和 子 shell 中都可以访问,如下图
6.1.2 局部环境变量
- 局部环境变量 只能在创建它们的会话中使用
- Linux 同样内置了很多默认的 局部环境变量
6.1.2.1 set 命令查看局部环境变量
- Linux 没有内置专门用来查看 局部环境变量 的命令
- 使用
set
命令只是可以在执行结果中顺便看到 局部环境变量,如下图- 除了 局部环境变量 ,还会显示 全局环境变量 ,以及用户自定义环境变量
-
显示结果是按字母排序的
6.2 设置用户定义变量
6.2.1 设置局部用户定义变量
- 定义局部用户变量不需要使用任何命令,直接定义一个变量名,并通过等号赋值即可,如下图
- 定义变量并赋值后,可以使用
echo $variable
命令来显示变量值 -
如果变量值存在空格,就不能直接赋值,需要将变量值用双引号包裹
- 定义变量并赋值后,可以使用
6.2.1.1 设置变量的语法规范
- 需要注意的是 ,大部分编程语言中,在变量名、等号和值之间添加空格,被认为是一种松散且美观的编码方式
- 但是在 shell 中,这一点行不通,shell 中采用的是尽量紧凑的编码方式,变量名、等号和值之间不能有空格
- 但是的但是,这一个要点,在 shell 中也没有被彻底贯彻,就比如第 5 章中介绍到的
coproc 命令
,如果要在使用coproc
命令的同时自定义协程名称,则需要使用coproc coprocName { command; }
命令,花括号中的空格是必不可少的,否则是语法错误 - 我认为,这可能跟编写这些语法的大神们自身的风格有关,谁知道呢?
6.2.1.2 局部变量父子间无法通信
- 在 父 shell 中定义的局部变量,无法在 子 shell 中访问,如下图
-
echo
命令在 子 shell 中尝试访问在 父 shell 中定义的变量,得到的执行结果是一行空白
-
- 相同的,在 子 shell 中定义的局部变量,也无法在 父 shell 中访问,如下图
6.2.2 export 命令设置全局环境变量
- 使用
export
命令并不能直接定义 全局环境变量 - 需要先定义一个 局部环境变量 ,再使用
export
命令将这个变量提升为 全局环境变量 ,如下图- 在 父 shell 中定义的局部变量,使用
export
命令提升后,在 子 shell 中也能够顺利访问 -
需要注意的是 ,使用
export variable
命令时,变量名前面不需要在美元符号
- 在 父 shell 中定义的局部变量,使用
6.2.2.1 自定义的全局变量在子 shell 中是只读的
- 由 父 shell 定义的 全局环境变量 ,任何 子 shell 都可以访问,但无法修改,如下图
- 在 父 shell 中定义变量并提升后,在 子 shell 中可以访问
- 在 子 shell 中对该变量进行重新赋值并提升后,退出 子 shell ,在 父 shell 中
6.3 unset 命令删除环境变量
- 使用
unset
命令可以删除定义的环境变量,如下图- 使用
unset variable
命令后,就无法使用echo $variable
访问到对应变量,说明变量已经被删除
- 使用
6.3.1 子 shell 无法删除父 shell 定义的全局变量
- 在 6.2.2.1 节中提到,父 shell 中定义的全局变量,对于子 shell 是只读的 ,所以 子 shell 自然也无法删除 父 shell 定义的全局变量,如下图
- 在 子 shell 中删除由 父 shell 定义的变量后,只是当前 子 shell 无法再访问该变量
- 但退出当前 子 shell 后,父 shell 依旧能够顺利访问到该变量,说明该变量对于 子 shell 是只读的
6.4 默认的 shell 环境变量
- 默认情况下,Linux 会提供一些特定的环境变量用于定义 系统环境变量
- bash shell 的部分 系统环境变量 ,继承自 Unix Bourne shell ,例如 HOME 、PATH
- bash shell 自身也定义了很多 系统环境变量 ,例如 HISTSIZE 、BASH
6.5 设置 PATH 环境变量
-
PATH 是一个很关键的 系统环境变量 ,它定义了 用于进行命令和程序查找的目录 ,如下图
- 在第 5 章介绍过 shell 命令分为 内建命令 和 外部命令
- 我们之所以能直接使用 外部命令 ,就是因为在 PATH 中将存放 外部命令 的目录进行了指定
- PATH 中的不同目录,使用分号进行分隔
- 如果某个命令或程序的位置不包含在 PATH 指定的目录中,则无法进行全局调用
6.5.1 为 PATH 添加自定义目录
- PATH 中的目录默认情况下都是系统预置的,一般就包括用于存放 外部命令 的 /bin 、/usr/bin 、/sbin 、/usr/sbin 目录
- 如果想要让其他程序的命令也能够全局访问,例如安装 JDK 环境后一般都需要配置环境变量
- 只需要使用
PATH=$PATH:customCommandDirectory
即可- 这句命令的意思就是为 PATH 变量追加一个自定义的命令目录,相当于其他语言中的
+=
操作
- 这句命令的意思就是为 PATH 变量追加一个自定义的命令目录,相当于其他语言中的
- 不过这种直接在 shell 会话中执行的添加操作,是一次性的 ,如果当前会话退出,或系统重启,这一次的配置操作也会随之失效
6.6 定位系统环境变量
- 上一节介绍到为 PATH 添加自定义目录,但实现的效果是一次性的
- 要了解如何让自定义的环境变量持久化存在与系统中,需要先了解 shell 的三种启动方式
- 登录式 shell :登录时作为默认 shell 启动
-
交互式 shell :在当前 shell 会话中通过
bash
命令或zsh
命令启动的各种类型 子 shell - 非交互式 shell :在当前 shell 会话中通过脚本运行的 shell
6.6.1 登录式 shell
- 登录 Linux 时,启动的 shell 就叫做 登录式 shell
- 当 登录式 shell 启动时,首先会尝试读取以下 5 个不同的启动文件
6.6.1.1 /etc/profile 文件的作用
- /etc/profile 文件作为 shell 默认的主启动文件,主要是在系统启动时,做一些默认操作
- 同时会在文件末尾扫描指定的目录,用于加载可能存在的自定义启动文件
- 例如在 CentOS 的 /etc/profile 中,末尾有如下一段代码
- 这段代码的大概意思就是会扫描 /etc/profile.d 目录下的所有 .sh 文件
- 所以一般推荐将一些自定义的全局环境变量、启动文件放置在该目录下
- 如果偷懒直接在 /etc/profile 文件中进行自定义修改,在系统升级后,可能该文件会被重置,那么之前做的自定义修改也就失效了
for i in /etc/profile.d/*.sh ; do
if [ -r "$i" ]; then
if [ "${-#*i}" != "$-" ]; then
. "$i"
else
. "$i" >/dev/null 2>&1
fi
fi
done
- 不过在 macOS 的 /etc/profile 中,末尾没有上述这段代码,而是下面这段代码
- 在文件末尾有一个判断,大概意思是说:如果当前的 shell 类型是 bash shell ,则加载 bashrc 配置
- 由于我的 macOS 环境下使用的是 zsh shell ,所以输出
${BASH-no}
的结果是 no ,那么 bashrc 配置自然也不会加载
6.6.1.2 存放在 $HOME 目录下的用户启动文件
-
$HOME 目录其实指的就是用户根目录,这是一个默认的 系统环境变量 ,如下图
- 该目录下的启动文件作用都一样,提供一个用户专属的启动文件用于定义该用户会用到的环境变量
- 一般情况下,Linux 系统中都不会内置全部的这四个文件,通常只存在一到两个
-
在 macOS 中的查询结果,就只有两个,如下图
-
在 CentOS 中的查询结果,也只有两个,如下图
- 会被 shell 直接扫描的文件只有 .bash_profile 、.bash_login 、.profile
- 需要注意的是 ,扫描过程中,并不是三个文件都一定会被扫描到
- 扫描的规则是,运行第一个被找到的文件,其他的直接忽略
- 这可能就是在上述两个系统环境中都没有找到 .bash_login 和 .profile 文件的原因吧,我猜的
-
.bashrc 文件不会被直接扫描的原因是因为该文件通常会被其他文件执行
- 在 CentOS 中被 .bash_profile 执行
- 在 macOS 中被 /etc/profile 执行
6.6.2 交互式 shell 进程
- 在 登录式 shell 中通过对应类型的 shell 命令启动的 shell ,就叫做 交互式 shell
-
交互式 shell 启动时,不会访问 /etc/profile 文件,会直接访问 $HOME/.bashrc 文件
- 前提是这个 shell 的类型是 bash shell ,如果是其他 shell ,则访问对应类型 shell 的启动文件
- 例如前文中说到的 zsh shell ,就会访问 $HOME/.zshrc 文件
-
.bashrc 的作用如下
- 执行 /etc 目录下通用的 bashrc 文件
- 为用户提供一个 自定义命令别名( alias ) ,以及 私有脚本函数 的位置
6.6.3 非交互式 shell
- 在当前 shell 会话中通过脚本运行的 shell 就叫做 非交互式 shell
-
非交互式 shell 在执行时,不会直接去访问任何的启动文件,而是直接继承当前执行环境的 系统环境变量 以及 全局环境变量
- 与 子 shell 和 父 shell 之间局部变量无法通信一样
- 非交互式 shell 也无法访问 父 shell 中没有被提升的局部变量
6.6.4 环境变量持久化
- 在前文的 6.6.1.1 节中就有提到,将自定义的全局环境变量放置在 /etc/profile 中不是一个好主意
- 而是应该放置在 /etc/profile.d 中,该目录是 CentOS 专门用来存放用户自定义 .sh 启动文件的位置
- $HOME/.bashrc 是存储自定义 bash shell 变量的位置,如果要存放 zsh shell ,则需要使用 $HOME/.zshrc
6.7 数组变量
- 数组变量并不实用,就好像所有的编程语言都应该拥有数组一样,在 shell 中,它只是被支持,但不常用
6.7.1 数组变量的基本语法
- 要定义一个数组变量,只需要在等号后面将多个用空格分隔的值,使用括号包裹即可,如下图
- 下图的运行环境是 CentOS
-
可以看到,同样一句话,使用双引号包裹,就是字符串,使用括号包裹就是数组,而且直接输出的结果不一样
- 实测发现 macOS 输出的结果和 CentOS 不一样,毕竟 macOS 是基于 Unix 开发,而不是 Linux ,如下图
- 在 macOS 中直接输出数组,也可以得到和字符串一样的输出结果
-
同时使用索引访问数组成员时,第 0 位是空,第 1 位才有值
6.7.2 通过索引访问数组成员
- 在上一节的第一个图中,可以看到直接访问数组的结果是默认输出第一个值
- 那么如果要访问数组的其他成员,则需要使用索引,如下图
- 索引从 0 开始,而且不支持负值
- 可以看到和上一节第二个图中 macOS 的查询效果差别很大
-
花括号和变量名直接也需要是紧凑的,不能有空格
6.7.3 星号显示数组所有成员
- 要实现和字符串一样的输出效果,可以在指定索引的位置使用一个星号,如下图
-
这一操作 macOS 也支持
-
6.7.4 修改数组指定索引的值
- 要修改数组指定索引的值,只需要直接为数组指定索引的成员赋值即可,如下图
- 一个索引只能只能一个值,不能指定多个值
-
但可以通过双引号将多个值标记为普通字符串,就可以实现类似效果
6.7.5 unset 命令删除数组指定索引的值
- 使用
unset
命令可以删除数组指定索引的值,但这个删除操作并不会更新数组的索引排序,如下图- 可以看到,使用
unset mySigns[1]
后,之前位于该索引的值就不存在了 - 但使用
echo ${mySigns[1]}
尝试输出后,得到的是一个空值,而不是之前位于索引 2 位置的值 -
这一点就和其他编程语言不一样,例如 Java 和 Javascript ,在这些语言中,如果索引 1 的值被删掉,数组的索引排序将会更新,再次访问索引 1 时,会得到之前位于索引 2 的值
- 可以看到,使用
6.8 小结
- Linux 中的环境变量分为 全局环境变量 和 局部环境变量
- 全局环境变量 可以在父子级之间访问,但父级定义的 全局环境变量 对于子级来说是只读的
- 局部环境变量 无法在父子级之间访问,只能在定义该变量的会话中访问
- PATH 是一个很关键的 全局环境变量 ,也叫 系统环境变量 ,其为 shell 执行各种命令指定了搜索目录
- PATH 支持自定义修改,可以将各种不是系统自带的命令加入其中,从而实现命令的全局访问
- shell 的启动方式分为三种,分别是 登录式 shell 、交互式 shell 、非交互 shell
- /etc/profile 是 shell 的主启动文件,在 登录式 shell 启动时会被访问
- .bashrc 是用于存放用户自定义变量的持久化文件,在 登录式 shell 和 交互式 shell 启动时会被访问
- 环境变量数组 是一个即不好用,也不常用的特性