Linux 命令行与 shell 脚本编程大全 6 使用 Linux 环境变量

Linux 环境变量能提升 shell 使用体验
很多程序和脚本都通过环境变量获取系统信息、存储临时数据和配置信息

更多精彩

6.1 什么是环境变量

  1. shell 中用来存储有关 shell 会话和工作环境的变量,被叫做 环境变量( Environment Variable )
  2. 环境变量 被存储在内存中,方便程序或在 shell 中运行的脚本能轻松访问
  3. shell 中的环境变量分为两类
    • 全局环境变量
    • 局部环境变量

6.1.1 全局环境变量

  1. 全局环境变量 之所以被叫做 全局 ,是因为其对于 父 shell 和所有生成的 子 shell 会话都是可见的
    • 局部环境变量 只对创建它们的 shell 可见
  2. Linux 内置了很多默认的 全局环境变量 ,会在用户登录 shell 时依次加载,这种 全局环境变量 被叫做 系统环境变量
  3. 系统环境变量 基本上都是使用全大写字母,用来区别用户自行创建的环境变量

6.1.1.1 env 命令和 printenv 命令查看全局环境变量

  1. 使用 env 命令和 printenv 命令可以查看当前系统的全局环境变量,如下图

  2. printenv 命令和 env 命令的区别在于,printenv 命令可以查看单个变量,而 env 命令不行,如下图

6.1.1.2 echo 命令查看全局环境变量

  1. 使用 echo 命令也可以查看单个变量的值,但语法稍微复杂一点,需要在变量名称前加一个美元符号 ,如下图

6.1.1.3 全局环境变量的全局观

  1. 前面说过,全局环境变量 之所以被叫做 全局 ,是因为在 父 shell子 shell 中都可以访问,如下图

6.1.2 局部环境变量

  1. 局部环境变量 只能在创建它们的会话中使用
  2. Linux 同样内置了很多默认的 局部环境变量

6.1.2.1 set 命令查看局部环境变量

  1. Linux 没有内置专门用来查看 局部环境变量 的命令
  2. 使用 set 命令只是可以在执行结果中顺便看到 局部环境变量,如下图
    • 除了 局部环境变量 ,还会显示 全局环境变量 ,以及用户自定义环境变量
    • 显示结果是按字母排序的


6.2 设置用户定义变量

6.2.1 设置局部用户定义变量

  1. 定义局部用户变量不需要使用任何命令,直接定义一个变量名,并通过等号赋值即可,如下图
    • 定义变量并赋值后,可以使用 echo $variable 命令来显示变量值
    • 如果变量值存在空格,就不能直接赋值,需要将变量值用双引号包裹


6.2.1.1 设置变量的语法规范

  1. 需要注意的是 ,大部分编程语言中,在变量名、等号和值之间添加空格,被认为是一种松散且美观的编码方式
  2. 但是在 shell 中,这一点行不通,shell 中采用的是尽量紧凑的编码方式,变量名、等号和值之间不能有空格
  3. 但是的但是,这一个要点,在 shell 中也没有被彻底贯彻,就比如第 5 章中介绍到的 coproc 命令 ,如果要在使用 coproc 命令的同时自定义协程名称,则需要使用 coproc coprocName { command; } 命令,花括号中的空格是必不可少的,否则是语法错误
  4. 我认为,这可能跟编写这些语法的大神们自身的风格有关,谁知道呢?

6.2.1.2 局部变量父子间无法通信

  1. 父 shell 中定义的局部变量,无法在 子 shell 中访问,如下图
    • echo 命令在 子 shell 中尝试访问在 父 shell 中定义的变量,得到的执行结果是一行空白
  2. 相同的,在 子 shell 中定义的局部变量,也无法在 父 shell 中访问,如下图

6.2.2 export 命令设置全局环境变量

  1. 使用 export 命令并不能直接定义 全局环境变量
  2. 需要先定义一个 局部环境变量 ,再使用 export 命令将这个变量提升为 全局环境变量 ,如下图
    • 父 shell 中定义的局部变量,使用 export 命令提升后,在 子 shell 中也能够顺利访问
    • 需要注意的是 ,使用 export variable 命令时,变量名前面不需要在美元符号

6.2.2.1 自定义的全局变量在子 shell 中是只读的

  1. 父 shell 定义的 全局环境变量 ,任何 子 shell 都可以访问,但无法修改,如下图
    • 父 shell 中定义变量并提升后,在 子 shell 中可以访问
    • 子 shell 中对该变量进行重新赋值并提升后,退出 子 shell ,在 父 shell

6.3 unset 命令删除环境变量

  1. 使用 unset 命令可以删除定义的环境变量,如下图
    • 使用 unset variable 命令后,就无法使用 echo $variable 访问到对应变量,说明变量已经被删除

6.3.1 子 shell 无法删除父 shell 定义的全局变量

  1. 在 6.2.2.1 节中提到,父 shell 中定义的全局变量,对于子 shell 是只读的 ,所以 子 shell 自然也无法删除 父 shell 定义的全局变量,如下图
    • 子 shell 中删除由 父 shell 定义的变量后,只是当前 子 shell 无法再访问该变量
    • 但退出当前 子 shell 后,父 shell 依旧能够顺利访问到该变量,说明该变量对于 子 shell 是只读的

6.4 默认的 shell 环境变量

  1. 默认情况下,Linux 会提供一些特定的环境变量用于定义 系统环境变量
  2. bash shell 的部分 系统环境变量 ,继承自 Unix Bourne shell ,例如 HOMEPATH
  3. bash shell 自身也定义了很多 系统环境变量 ,例如 HISTSIZEBASH

6.5 设置 PATH 环境变量

  1. PATH 是一个很关键的 系统环境变量 ,它定义了 用于进行命令和程序查找的目录 ,如下图
    • 在第 5 章介绍过 shell 命令分为 内建命令外部命令
    • 我们之所以能直接使用 外部命令 ,就是因为在 PATH 中将存放 外部命令 的目录进行了指定
  2. PATH 中的不同目录,使用分号进行分隔
  3. 如果某个命令或程序的位置不包含在 PATH 指定的目录中,则无法进行全局调用

6.5.1 为 PATH 添加自定义目录

  1. PATH 中的目录默认情况下都是系统预置的,一般就包括用于存放 外部命令/bin/usr/bin/sbin/usr/sbin 目录
  2. 如果想要让其他程序的命令也能够全局访问,例如安装 JDK 环境后一般都需要配置环境变量
  3. 只需要使用 PATH=$PATH:customCommandDirectory 即可
    • 这句命令的意思就是为 PATH 变量追加一个自定义的命令目录,相当于其他语言中的 += 操作
  4. 不过这种直接在 shell 会话中执行的添加操作,是一次性的 ,如果当前会话退出,或系统重启,这一次的配置操作也会随之失效

6.6 定位系统环境变量

  1. 上一节介绍到为 PATH 添加自定义目录,但实现的效果是一次性的
  2. 要了解如何让自定义的环境变量持久化存在与系统中,需要先了解 shell 的三种启动方式
    • 登录式 shell :登录时作为默认 shell 启动
    • 交互式 shell :在当前 shell 会话中通过 bash 命令或 zsh 命令启动的各种类型 子 shell
    • 非交互式 shell :在当前 shell 会话中通过脚本运行的 shell

6.6.1 登录式 shell

  1. 登录 Linux 时,启动的 shell 就叫做 登录式 shell
  2. 登录式 shell 启动时,首先会尝试读取以下 5 个不同的启动文件

6.6.1.1 /etc/profile 文件的作用

  1. /etc/profile 文件作为 shell 默认的主启动文件,主要是在系统启动时,做一些默认操作
  2. 同时会在文件末尾扫描指定的目录,用于加载可能存在的自定义启动文件
  3. 例如在 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
  1. 不过在 macOS 的 /etc/profile 中,末尾没有上述这段代码,而是下面这段代码
    • 在文件末尾有一个判断,大概意思是说:如果当前的 shell 类型是 bash shell ,则加载 bashrc 配置
    • 由于我的 macOS 环境下使用的是 zsh shell ,所以输出 ${BASH-no} 的结果是 no ,那么 bashrc 配置自然也不会加载

6.6.1.2 存放在 $HOME 目录下的用户启动文件

  1. $HOME 目录其实指的就是用户根目录,这是一个默认的 系统环境变量 ,如下图
  2. 该目录下的启动文件作用都一样,提供一个用户专属的启动文件用于定义该用户会用到的环境变量
  3. 一般情况下,Linux 系统中都不会内置全部的这四个文件,通常只存在一到两个
  4. 在 macOS 中的查询结果,就只有两个,如下图


  5. 在 CentOS 中的查询结果,也只有两个,如下图


  6. 会被 shell 直接扫描的文件只有 .bash_profile.bash_login.profile
    • 需要注意的是 ,扫描过程中,并不是三个文件都一定会被扫描到
    • 扫描的规则是,运行第一个被找到的文件,其他的直接忽略
    • 这可能就是在上述两个系统环境中都没有找到 .bash_login.profile 文件的原因吧,我猜的
  7. .bashrc 文件不会被直接扫描的原因是因为该文件通常会被其他文件执行
    • 在 CentOS 中被 .bash_profile 执行
    • 在 macOS 中被 /etc/profile 执行

6.6.2 交互式 shell 进程

  1. 登录式 shell 中通过对应类型的 shell 命令启动的 shell ,就叫做 交互式 shell
  2. 交互式 shell 启动时,不会访问 /etc/profile 文件,会直接访问 $HOME/.bashrc 文件
    • 前提是这个 shell 的类型是 bash shell ,如果是其他 shell ,则访问对应类型 shell 的启动文件
    • 例如前文中说到的 zsh shell ,就会访问 $HOME/.zshrc 文件
  3. .bashrc 的作用如下
    1. 执行 /etc 目录下通用的 bashrc 文件
    2. 为用户提供一个 自定义命令别名( alias ) ,以及 私有脚本函数 的位置

6.6.3 非交互式 shell

  1. 在当前 shell 会话中通过脚本运行的 shell 就叫做 非交互式 shell
  2. 非交互式 shell 在执行时,不会直接去访问任何的启动文件,而是直接继承当前执行环境的 系统环境变量 以及 全局环境变量
    • 子 shell父 shell 之间局部变量无法通信一样
    • 非交互式 shell 也无法访问 父 shell 中没有被提升的局部变量

6.6.4 环境变量持久化

  1. 在前文的 6.6.1.1 节中就有提到,将自定义的全局环境变量放置在 /etc/profile 中不是一个好主意
  2. 而是应该放置在 /etc/profile.d 中,该目录是 CentOS 专门用来存放用户自定义 .sh 启动文件的位置
  3. $HOME/.bashrc 是存储自定义 bash shell 变量的位置,如果要存放 zsh shell ,则需要使用 $HOME/.zshrc

6.7 数组变量

  1. 数组变量并不实用,就好像所有的编程语言都应该拥有数组一样,在 shell 中,它只是被支持,但不常用

6.7.1 数组变量的基本语法

  1. 要定义一个数组变量,只需要在等号后面将多个用空格分隔的值,使用括号包裹即可,如下图
    • 下图的运行环境是 CentOS
    • 可以看到,同样一句话,使用双引号包裹,就是字符串,使用括号包裹就是数组,而且直接输出的结果不一样


  2. 实测发现 macOS 输出的结果和 CentOS 不一样,毕竟 macOS 是基于 Unix 开发,而不是 Linux ,如下图
    • 在 macOS 中直接输出数组,也可以得到和字符串一样的输出结果
    • 同时使用索引访问数组成员时,第 0 位是空,第 1 位才有值


6.7.2 通过索引访问数组成员

  1. 在上一节的第一个图中,可以看到直接访问数组的结果是默认输出第一个值
  2. 那么如果要访问数组的其他成员,则需要使用索引,如下图
    • 索引从 0 开始,而且不支持负值
    • 可以看到和上一节第二个图中 macOS 的查询效果差别很大
    • 花括号和变量名直接也需要是紧凑的,不能有空格

6.7.3 星号显示数组所有成员

  1. 要实现和字符串一样的输出效果,可以在指定索引的位置使用一个星号,如下图
    • 这一操作 macOS 也支持


6.7.4 修改数组指定索引的值

  1. 要修改数组指定索引的值,只需要直接为数组指定索引的成员赋值即可,如下图
    • 一个索引只能只能一个值,不能指定多个值
    • 但可以通过双引号将多个值标记为普通字符串,就可以实现类似效果


6.7.5 unset 命令删除数组指定索引的值

  1. 使用 unset 命令可以删除数组指定索引的值,但这个删除操作并不会更新数组的索引排序,如下图
    • 可以看到,使用 unset mySigns[1] 后,之前位于该索引的值就不存在了
    • 但使用 echo ${mySigns[1]} 尝试输出后,得到的是一个空值,而不是之前位于索引 2 位置的值
    • 这一点就和其他编程语言不一样,例如 Java 和 Javascript ,在这些语言中,如果索引 1 的值被删掉,数组的索引排序将会更新,再次访问索引 1 时,会得到之前位于索引 2 的值


6.8 小结

  1. Linux 中的环境变量分为 全局环境变量局部环境变量
  2. 全局环境变量 可以在父子级之间访问,但父级定义的 全局环境变量 对于子级来说是只读的
  3. 局部环境变量 无法在父子级之间访问,只能在定义该变量的会话中访问
  4. PATH 是一个很关键的 全局环境变量 ,也叫 系统环境变量 ,其为 shell 执行各种命令指定了搜索目录
  5. PATH 支持自定义修改,可以将各种不是系统自带的命令加入其中,从而实现命令的全局访问
  6. shell 的启动方式分为三种,分别是 登录式 shell交互式 shell非交互 shell
  7. /etc/profile 是 shell 的主启动文件,在 登录式 shell 启动时会被访问
  8. .bashrc 是用于存放用户自定义变量的持久化文件,在 登录式 shell交互式 shell 启动时会被访问
  9. 环境变量数组 是一个即不好用,也不常用的特性
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342