Linux环境变量(export、echo、/etc/profile、~/.bashrc辨析)(上)

本文最后更新于:2024年8月2日 下午

由于本人遇到问题经常不求甚解,导致学艺不精。所以每次用Shell指令时,在遇到export、echo、/etc/profile、~/.bashrc等常见的指令和文件时都会有一种“诶好像在哪见过,但我忘了”的感觉,然后再照着教程敲命令,所谓“小和尚念经——有口无心”,正是本人了。好了,本着事不过三的原则,这次下点功夫,索性把这些东西全给他端了。

在开始之前,先介绍Linux中的一些变量,因为它们经常遇到,并且环境变量在Shell中起着举足轻重的作用。

普通变量(自定义变量)

我将Shell普通变量理解为C语言中变量,可以用来完成幅值、运算等操作,字符串变量还能完成拼接、打印等各种函数操作。一般来说,普通变量一般由开发者在开发脚本程序时创建,完成运算、判断等任务。 例如,我创建一个名为test1.sh的脚本,在其中敲入以下内容:

1
2
3
4
#!/bin/bash
for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done
为其添加可执行权限后使用./test1.sh指令执行该脚本,输出如下:
1
2
3
4
5
6
yeyisheng@ubuntu:~$ ./test1.sh 
I am good at AdaScript
I am good at CoffeScript
I am good at ActionScript
I am good at JavaScript
yeyisheng@ubuntu:~$
其中,echo是将内容在terminal打印出来的指令,$是用来获取变量值的,在这里就是获取变量skill的值,其两边的花括号是为了帮助解释器识别变量的边界[1],如果不给skill变量加花括号,写成echo "I am good at skillScript",解释器就会把skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。推荐给所有变量加上花括号,这是个好的编程习惯

需要注意的是,Bash中的变量默认都是字符串型变量,如果我们想当然地定义一个“数值变量”,并对其进行加减乘除等算数运算,会发现不能得到预期中的结果,如以下代码:

1
2
3
4
5
#!/bin/bash
a=123
b=456
c=$a+$b
echo $c
最后打印的结果是123+456,如果想使变量支持数值运算,需要使用declare命令手动声明变量的类型[2]。在这里还可以看出,声明变量时变量名与等号之间不能有等号,这可能和你熟悉的所有编程语言都不一样,等号两边的空格可能会导致错误[3]

declare的使用语法是:

1
declare [+/-] [选项] 变量名称
图1 declare参数说明

具体的关于Shell变量和编程方法这里就不再赘述,详情见以下三篇文章: Linux Bash Shell编程 Shell变量 Linux基础概要

普通变量的生存时间有限,只能在当前脚本文件或当前Shell中使用,无法在当前Shell的子Shell中使用,因为这些变量没有添加进Shell的环境中,也就是说它们并不是环境变量。以上面的脚本为例,在脚本执行完成后,我想单独打印出a、b、c的值,却只能打印出空白: 图2 普通变量

./指令会另外打开一个Shell进行可执行文件的执行,由于a、b、c并不是环境变量,所以在其他Shell中无法使用。

关于source.shbash./的辨析,可以看我的这篇文章:脚本执行指令辨析(source , . , sh , bash , ./)

环境变量

当我们启动Shell的时候,会引用一组变量以确保正确配置Shell。这些变量还确保终端窗口和Shell可能需要参考的任何信息都可用。总的来说,这些变量包含定义我们在终端窗口中找到的环境的设置,从我们写完代码后调用的GCC编译器位置、Shell尝试查找命令时搜索的目录集以及我们的默认编辑器,一直到我们的语言环境、时区、键盘设置和命令提示符的外观。因此,很自然地,它们被称为环境变量[4]

环境变量的继承

区别于普通变量,环境变量可以在当前Shell的子Shell中继续有效,而普通变量的则无法在其子Shell中生效。(父Shell与子Shell的关系等效于脚本执行指令辨析(source , . , sh , bash , ./)提到的父程序与子程序) 当Shell启动时(打开Terminal或者执行某个脚本)时,它会经历一个初始化阶段,此时Shell会读取定义Shell环境的环境变量。 如果在Terminal中执行一个程序、脚本或者命令时,当前Shell/进程会开辟一个子Shell/进程,子Shell会进程父进程的环境变量。

以下的几种环境变量是我在查阅资料后自己总结出的,名称也是我自己起的,严谨起见,我会在文章末尾贴出所有用到的参考网页链接

全用户环境变量

顾名思义,全用户环境变量可以被所有用户使用,以下是几种常见的全用户环境变量:

  • SHELL: 打开终端窗口时将启动的 shell 的名称。在大多数 Linux 发行版中,这将是 bash,除非您更改了默认值。
  • TERM:终端窗口实际上是硬件终端的模拟。这包含将要模拟的硬件终端的类型。
  • USER:当前使用该系统的人的用户名。
  • PWD:当前工作目录的路径。
  • OLDPWD:移动到当前工作目录之前您所在的目录。
  • LS_COLORS:ls 使用的颜色代码列表突出显示不同的文件类型。
  • MAIL:如果 mail 系统已在您的 Linux 计算机上设置(默认情况下未设置),这将保存当前用户的路径邮箱。
  • PATH:一个目录列表,shell 将搜索这些目录以查找命令可执行文件。
  • LANG:语言、本地化和字符编码设置。
  • HOME:当前用户的主目录。
  • :下划线 () 环境变量保存最后输入的命令。

如果我们需要自定义一个全用户环境变量,可以将它们添加到/etc/environment文件中。注意需要使用sudo来编辑这个文件:

1
sudo vi /etc/environment
往其中添加一个名为USER1的变量,注意需要关机重启或者使用source指令和.指令该文件后才能使用这个变量:

图3 添加全用户环境变量
图4 用户yeyisheng使用变量

因为它是一个全局环境变量,并且对所有人都可用,所以用户yezhixuan可以在下次登录时引用该环境变量: 图5 用户yezhixuan使用变量

SHELL环境变量

这些是 bash 中用于指示或记录其行为和功能的一些 shell 环境变量。一些值会在您使用终端时更新。例如,COLUMNS环境变量将被更新以反映您可能对终端窗口宽度所做的更改:

  • BASHOPTS:启动 bash 时使用的命令行选项。
  • BASH_VERSION:bash 版本号,由单词和数字组成。
  • BASH_VERSINFO:作为数字的 bash 版本。
  • COLUMNS:终端窗口的当前宽度。
  • DIRSTACK:已通过pushd命令添加到目录堆栈的目录。
  • HISTFILESIZE:history文件中允许的最大行数。
  • HISTSIZE:内存中允许的history行数。
  • HOSTNAME:计算机的主机名。
  • IFS:用于分隔命令行输入的内部字段分隔符。默认情况下,这是一个空格。
  • PS1:PS1 环境变量包含主要提示符、默认提示符和命令提示符的定义。命令提示符的定义中可以包含一组称为转义序列的标- 记。它们代表主机和用户名、当前工作目录和时间等内容。
  • PS2:当一条命令超过一行并且需要更多输入时,会显示辅助命令提示符。PS2环境变量保存此辅助提示的定义,默认情况下,它是大于号 (>)。
  • SHELLOPTS:可以使用set选项设置的Shell选项。
  • UID:当前用户的用户标识符。

这些变量就好比是Shell的出厂说明书,告诉你Shell的配置信息和如何对其进行修改。

Terminal对话环境变量

Terminal对话环境变量主要是我们平时在打开Terminal之后在对话中会使用到的一些环境变量,比如在编译代码时用到的编译器的路径等等。 如果只想创建单一用户使用的环境变量,而不是让计算机上的每个用户都受到某一个用户创建的环境变量的影响,我们可以在.bashrc(bash runtime configuration,bash运行时配置文件)文件中添加属于单一用户的环境变量,该文件在用户的家目录下,文件名称最前面的.代表这是一个隐藏文件,我们需要使用指令ls -a才能查看它。 图6 .bashrc

比如我在开发Qt时就在这个文件里面导入了下面几个关于环境配置的环境变量。 图7 Qt配置的环境变量

事实上,.bashrc文件是每一位用户用来配置终端功能和属性设置的,修改.bashrc可以改变环境变量PATH、别名alias和提示符。Terminal使用Bash对用户输入的指令进行解释并执行,且允许使用脚本进行一定程度的自定义,这就是.bashrc配置文件的作用。为了加载用户首选项Bash在每次启动时都会自动载入.bashrc配置文件中的内容(无论是新打开一个Shell还是在父Shell中打开一个子Shell),它用于保存和加载不同用户的终端首选项环境变量[5]

自定义.bashrc配置文件的主要好处有: - 添加别名可以让更快地输入和执行命令,以节省时间。 - 添加函数可以保存和重复执行复杂的代码。 - 可以显示有用的系统信息。 - 可以自定义 Bash 提示信息。

需要注意的是,用户对 .bashrc所作的任何更改将在下次启动终端时生效,如果想立即生效可以手动执行source命令或.命令重新读取文件进行刷新。推荐阅读文章bashrc 配置文件自定义指南,如何添加别名、使用函数等以便获得对.bashrc文件更加全面和深入的认识。

扯远了,让我们回到对话环境变量。我们分别创建一个普通变量和一个环境变量,同时我还额外创建了一个SESSSION_VAR变量: 图8 验证环境变量

图9 验证环境变量

export指令用来创建能够被子Shell继承的环境变量,我将在稍后介绍这个指令。

一个变量创建时,它不会自动地为在它之后创建的shell进程所知。而命令export可以向后面的shell传递变量的值。当一个shell脚本调用并执行时,它不会自动得到原为脚本(调用者)里定义的变量的访问权,除非这些变量已经被显式地设置为可用。export命令可以用于传递一个或多个变量的值到任何后继脚本。 ----《UNIX教程》

说人话就是,变量在创建时如果不是使用export指令创建的,那么它就是一个普通变量,这个普通变量不能在其他Shell或当前Shell的子Shell中使用;如果使用了export指令创建,那么它就是一个环境变量,这个环境变量能在当前Shell的子Shell中使用。但它仍然不能在另一个Shell中使用,如果像使它在另一个Shell中使用,需要在.bashrc文件中export这个变量。

现在我们已经创建好了COMMON_VARSESSION_VARINHERITED_VAR这三个变量,接着我们在Terminal中将它们打印出来。 图10 验证环境变量

前三个echo分别打印出了上面三个变量,接着我使用bash指令打开了一个子Shell,(由于我在.bashrc中的设置,所以每打开一个Shell都会打印出User name和Date)。可以看出,INHERITED_VARSESSION_VAR均可在子shell中访问,但COMMON_VAR不可访问。我们只是得到一个空行。INHERITED_VAR是因为export指令导入为环境变量,所以能在子Shell中使用,这个我们能够理解,但是SESSION_VAR并没有导入为环境变量,它为什么也能在子Shell中使用呢?这个问题留给读者自己去思考(>_*。

注意,虽然INHERITED_VARexport指令导入为环境变量,但它不是一个全用户环境变量,所以用户yezhixuan并不能使用它。

要关闭我们的子bash会话,我们使用exit

1
exit

接着我们做一个有趣的小实验,创建一个名为envtest.sh的脚本,向其中写入如下内容:

1
2
3
4
5
#!/bin/bash

echo "SESSION_VAR" $SESSION_VAR
echo "INHERITED_VAR" $INHERITED_VAR
echo "COMMON_VAR" $COMMON_VAR
分别使用source.指令执行脚本,打印出来的内容却不大相同,为什么会这样?还记得我在上面曾说过的话吗:“./指令会另外打开一个Shell进行可执行文件的执行”,新打开的Shell同时丢失了SESSION_VARCOMMON_VAR这两个变量。 图11 验证变量的继承

如果我们在命令行导出这两个环境变量,然后再次运行脚本: 图12 验证脚本对变量的继承

环境变量已经添加到当前shell的环境中,因此它出现在脚本继承的环境中。该脚本也可以引用该环境变量。

远程连接时可以使用的环境变量

我没有在远程会话中使用过环境变量,所以在这里我只是简要介绍一下。(以后如果用到了我会接着进行补充

远程登录会话可以访问全局环境变量,但是如果您希望本地定义的环境变量可以远程使用,则必须将它们添加到 .bash_profile 文件中。您可以在 .bashrc 和 .bash_profile 文件中设置相同的环境变量,但值不同。这可以通过脚本获取,例如,为本地或远程使用系统的人修改其行为。

据我的理解,如果想要在远程连接时使用环境变量,需要提前在.bash_profile文件中写入。而且.bash_profile.bashrc二者相互独立,可以在.bash_profile.bashrc文件中设置名称相同但值不同的环境变量。

相关指令[6]

export

Linuxexport命令用于设置或显示环境变量。

在 shell 中执行程序时,shell 会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅限于该次登陆操作[7]

单独使用export命令会打印出当前系统定义的所有环境变量(关注红框部分的PATH变量) 图13 export指令 我们可以在打印出来的内容中看到诸如declare -x LANG="zh_CN.UTF-8"的字样,由之前我们对declare指令的介绍可以知道,参数x代表环境变量的意思。

使用指令export PATH=$PATH:/opt,再使用export指令查看当前系统定义的所有环境变量(关注红框部分的PATH变量,发现多了:/opt). 图14 export指令

env

env指令用来显示当前用户的环境变量,但不会显示其自定义变量

declare

declare指令用来显示当前Shell中定义的所有变量,包括用户的环境变量和自定义变量,该命令的输出按变量名进行排序。

set

set指令功能同declare一样,显示当前Shell中定义的所有变量,包括用户的环境变量和自定义变量。


Linux环境变量(export、echo、/etc/profile、~/.bashrc辨析)(上)
http://example.com/2024/06/15/Linux环境变量(export、echo、-etc-profile、-bashrc辨析/
作者
叶逸昇
发布于
2024年6月15日
许可协议