什么是环境变量
bash shell用一个叫作环境变量(environment variable)的特性来存储有关shell会话和工作环境的信息(这也是它们被称作环境变量的原因)。这项特性允许你在内存中存储数据,以便程序或shell中运行的脚本能够轻松访问到它们。这也是存储持久数据的一种简便方法。
在bash shell中,环境变量分为两类:
全局变量
局部变量
全局环境变量
全局环境变量对于shell会话和所有生成的子shell都是可见的。局部变量则只对创建它们的shell可见。这让全局环境变量对那些所创建的子shell需要获取父shell信息的程序来说非常有用。
Linux系统在你开始bash会话时就设置了一些全局环境变量。系统环境变量基本上都是使用全大写字母,以区别于普通用户的环境变量。要查看全局变量,可以使用env或printenv命令。
要显示个别环境变量的值,可以使用printenv命令,但是不要用env命令。也可以使用echo显示变量的值。在这种情况下引用某个环境变量的时候,必须在变量前面加上一个美元符($)。
$ printenv HOME
/home/Christine
$ env HOME
env: HOME: No such file or directory
$ echo $HOME
/home/Christine
在echo命令中,在变量名前加上$可不仅仅是要显示变量当前的值。它能够让变量作为命令行参数。
$ ls $HOME
/home/Christine
局部环境变量
局部环境变量只能在定义它们的进程中可见。尽管它们是局部的,但是和全局环境变量一样重要。事实上,Linux系统也默认定义了标准的局部环境变量。
查看局部环境变量的列表有点复杂。遗憾的是,在Linux系统并没有一个只显示局部环境变量的命令。set命令会显示为某个特定进程设置的所有环境变量,包括局部变量、全局变量以及用户定义变量。
命令env、printenv和set之间的差异很细微。set命令会显示出全局变量、局部变量以及用户定义变量。它还会按照字母顺序对结果进行排序。env和printenv命令同set命令的区别在于前两个命令不会对变量排序,也不会输出局部变量和用户定义变量。在这种情况下,env和printenv的输出是重复的。不过env命令有一个printenv没有的功能,这使得它要更有用一些。
设置用户定义变量
设置局部用户定义变量
一旦启动了bash shell(或者执行一个shell脚本),就能创建在这个shell进程内可见的局部变量了。可以通过等号给环境变量赋值,值可以是数值或字符串。
$ echo $my_variable
$ my_variable=Hello
如果要给变量赋一个含有空格的字符串值,必须用单(双)引号来界定字符串的首和尾。
记住,变量名、等号和值之间没有空格,这一点非常重要。
设置全局环境变量
在设定全局环境变量的进程所创建的子进程中,该变量都是可见的。创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局环境中。这个过程通过export命令来完成,变量名前面不需要加$。
$ my_variable="I am Global now"
$
$ export my_variable
修改子shell中全局环境变量并不会影响到父shell中该变量的值。在定义并导出变量my_variable后,bash命令启动了一个子shell。在这个子shell中能够正确显示出全局环境变量my_variable的值。子shell随后改变了这个变量的值。但是这种改变仅在子shell中有效,并不会被反映到父shell中。子shell甚至无法使用export命令改变父shell中全局环境变量的值。
尽管子shell重新定义并导出了变量my_variable,但父shell中的my_variable变量依然保留着原先的值。
删除环境变量
用unset命令完成这个操作。在unset命令中引用环境变量时,记住不要使用$。
在涉及环境变量名时,什么时候该使用$,什么时候不该使用$,实在让人摸不着头脑。
记住一点就行了:如果要用到变量,使用$;如果要操作变量,不使用$。这条规则的一个例外就是使用printenv显示某个变量的值。
在处理全局环境变量时,事情就有点棘手了。如果你是在子进程中删除了一个全局环境变量,这只对子进程有效。该全局环境变量在父进程中依然可用。
默认的 shell 环境变量
默认情况下,bash shell会用一些特定的环境变量来定义系统环境。这些变量在你的Linux系统上都已经设置好了,只管放心使用。bash shell源自当初的Unix Bourne shell,因此也保留了Unix Bourne shell里定义的那些环境变量。
表6-1 bash shell支持的Bourne变量
变 量 | 描 述 |
---|---|
CDPATH | 冒号分隔的目录列表,作为cd命令的搜索路径 |
HOME | 当前用户的主目录 |
IFS | shell用来将文本字符串分割成字段的一系列字符 |
当前用户收件箱的文件名(bash shell会检查这个文件,看看有没有新邮件) | |
MAILPATH | 冒号分隔的当前用户收件箱的文件名列表(bash shell会检查列表中的每个文件,看看有没有新邮件) |
OPTARG | getopts命令处理的最后一个选项参数值 |
OPTIND | getopts命令处理的最后一个选项参数的索引号 |
PATH | shell查找命令的目录列表,由冒号分隔 |
PS1 | shell命令行界面的主提示符 |
PS2 | shell命令行界面的次提示符 |
除了默认的Bourne的环境变量,bash shell还提供一些自有的变量。
变 量 | 描 述 |
---|---|
BASH | 当前shell实例的全路径名 |
BASH_ALIASES | 含有当前已设置别名的关联数组 |
BASH_ARGC | 含有传入子函数或shell脚本的参数总数的数组变量 |
BASH_ARCV | 含有传入子函数或shell脚本的参数的数组变量 |
BASH_CMDS | 关联数组,包含shell执行过的命令的所在位置 |
BASH_COMMAND | shell正在执行的命令或马上就执行的命令 |
BASH_ENV | 设置了的话,每个bash脚本会在运行前先尝试运行该变量定义的启动文件 |
BASH_EXECUTION_STRING | 使用bash -c选项传递过来的命令 |
BASH_LINENO | 含有当前执行的shell函数的源代码行号的数组变量 |
BASH_REMATCH | 只读数组,在使用正则表达式的比较运算符=~进行肯定匹配(positive match)时,包含了匹配到的模式和子模式 |
BASH_SOURCE | 含有当前正在执行的shell函数所在源文件名的数组变量 |
BASH_SUBSHELL | 当前子shell环境的嵌套级别(初始值是0) |
BASH_VERSINFO | 含有当前运行的bash shell的主版本号和次版本号的数组变量 |
BASH_VERSION | 当前运行的bash shell的版本号 |
BASH_XTRACEFD | 若设置成了有效的文件描述符(0、1、2),则’set -x’调试选项生成的跟踪输出可被重定向。通常用来将跟踪输出到一个文件中 |
BASHOPTS | 当前启用的bash shell选项的列表 |
BASHPID | 当前bash进程的PID |
COLUMNS | 当前bash shell实例所用终端的宽度 |
COMP_CWORD | COMP_WORDS变量的索引值,后者含有当前光标的位置 |
COMP_LINE | 当前命令行 |
COMP_POINT | 当前光标位置相对于当前命令起始的索引 |
COMP_KEY | 用来调用shell函数补全功能的最后一个键 |
COMP_TYPE | 一个整数值,表示所尝试的补全类型,用以完成shell函数补全 |
COMP_WORDBREAKS | Readline库中用于单词补全的词分隔字符 |
COMP_WORDS | 含有当前命令行所有单词的数组变量 |
COMPREPLY | 含有由shell函数生成的可能填充代码的数组变量 |
COPROC | 占用未命名的协进程的I/O文件描述符的数组变量 |
DIRSTACK | 含有目录栈当前内容的数组变量 |
EMACS | 设置为’t’时,表明emacs shell缓冲区正在工作,而行编辑功能被禁止 |
ENV | 如果设置了该环境变量,在bash shell脚本运行之前会先执行已定义的启动文件(仅用于当bash shell以POSIX模式被调用时) |
EUID | 当前用户的有效用户ID(数字形式) |
FCEDIT | 供fc命令使用的默认编辑器 |
FIGNORE | 在进行文件名补全时可以忽略后缀名列表,由冒号分隔 |
FUNCNAME | 当前执行的shell函数的名称 |
FUNCNEST | 当设置成非零值时,表示所允许的最大函数嵌套级数(一旦超出,当前命令即被终止) |
GLOBIGNORE | 冒号分隔的模式列表,定义了在进行文件名扩展时可以忽略的一组文件名 |
GROUPS | 含有当前用户属组列表的数组变量histchars 控制历史记录扩展,最多可有3个字符 |
HISTCMD | 当前命令在历史记录中的编号 |
HISTCONTROL | 控制哪些命令留在历史记录列表中 |
HISTFILE | 保存shell历史记录列表的文件名(默认是.bash_history) |
HISTFILESIZE | 最多在历史文件中存多少行 |
HISTTIMEFORMAT | 如果设置了且非空,就用作格式化字符串,以显示bash历史中每条命令的时间戳 |
HISTIGNORE | 由冒号分隔的模式列表,用来决定历史文件中哪些命令会被忽略 |
HISTSIZE | 最多在历史文件中存多少条命令 |
HOSTFILE | shell在补全主机名时读取的文件名称 |
HOSTNAME | 当前主机的名称 |
HOSTTYPE | 当前运行bash shell的机器 |
IGNOREEOF | shell在退出前必须收到连续的EOF字符的数量(如果这个值不存在,默认是1) |
INPUTRC | Readline初始化文件名(默认是.inputrc) |
LANG | shell的语言环境类别 |
LC_ALL | 定义了一个语言环境类别,能够覆盖LANG变量 |
LC_COLLATE | 设置对字符串排序时用的排序规则 |
LC_CTYPE | 决定如何解释出现在文件名扩展和模式匹配中的字符 |
LC_MESSAGES | 在解释前面带有$的双引号字符串时,该环境变量决定了所采用的语言环境设置 |
LC_NUMERIC | 决定着格式化数字时采用的语言环境设置 |
LINENO | 当前执行的脚本的行号 |
LINES | 定义了终端上可见的行数 |
MACHTYPE | 用“CPU-公司-系统”(CPU-company-system)格式定义的系统类型 |
MAPFILE | 一个数组变量,当mapfile命令未指定数组变量作为参数时,它存储了mapfile所读入的文本 |
MAILCHECK | shell查看新邮件的频率(以秒为单位,默认值是60) |
OLDPWD | shell之前的工作目录 |
OPTERR | 设置为1时,bash shell会显示getopts命令产生的错误 |
OSTYPE | 定义了shell所在的操作系统 |
PIPESTATUS | 含有前台进程的退出状态列表的数组变量 |
POSIXLY_CORRECT | 设置了的话,bash会以POSIX模式启动 |
PPID | bash shell父进程的PID |
PROMPT_COMMAND | 设置了的话,在命令行主提示符显示之前会执行这条命令 |
PROMPT_DIRTRIM | 用来定义当启用了\w或\W提示符字符串转义时显示的尾部目录名的数量。被删除的目录名会用一组英文句点替换 |
PS3 | select命令的提示符 |
PS4 | 如果使用了bash的-x选项,在命令行之前显示的提示信息 |
PWD | 当前工作目录 |
RANDOM | 返回一个0~32767的随机数(对其的赋值可作为随机数生成器的种子) |
READLINE_LINE | 当使用bind –x命令时,存储Readline缓冲区的内容 |
READLINE_POINT | 当使用bind –x命令时,表示Readline缓冲区内容插入点的当前位置 |
REPLY | read命令的默认变量 |
SECONDS | 自从shell启动到现在的秒数(对其赋值将会重置计数器) |
SHELL | bash shell的全路径名 |
SHELLOPTS | 已启用bash shell选项列表,列表项之间以冒号分隔 |
SHLVL | shell的层级;每次启动一个新bash shell,该值增加1 |
TIMEFORMAT | 指定了shell的时间显示格式 |
TMOUT | select和read命令在没输入的情况下等待多久(以秒为单位)。默认值为0,表示无限长 |
TMPDIR | 目录名,保存bash shell创建的临时文件 |
UID | 当前用户的真实用户ID(数字形式) |
设置 PATH 环境变量
当你在shell命令行界面中输入一个外部命令时,shell必须搜索系统来找到对应的程序。PATH环境变量定义了用于进行命令和程序查找的目录。
PATH中各个目录之间是用冒号分隔的。你只需引用原来的PATH值,然后再给这个字符串添加新目录就行了。
如果希望子shell也能找到你的程序的位置,一定要记得把修改后的PATH环境变量导出。
定位系统环境变量
在你登入Linux系统启动一个bash shell时,默认情况下bash会在几个文件中查找命令。这些文件叫作启动文件或环境文件。bash检查的启动文件取决于你启动bash shell的方式。启动bash shell有3种方式:
登录时作为默认登录shell
作为非登录shell的交互式shell
作为运行脚本的非交互shell
登录 shell
当你登录Linux系统时,bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令:
/etc/profile
$HOME/.bash_profile
$HOME/.bashrc
$HOME/.bash_login
$HOME/.profile
/etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行
这个启动文件。另外4个启动文件是针对用户的,可根据个人需求定制。
shell会按照按照下列顺序,运行第一个被找到的文件,余下的则被忽略:
$HOME/.bash_profile
$HOME/.bash_login
H
O
M
E
/
.
p
r
o
f
i
l
e
注
意
,
这
个
列
表
中
并
没
有
HOME/.profile 注意,这个列表中并没有
HOME/.profile注意,这个列表中并没有HOME/.bashrc文件。这是因为该文件通常通过其他文件运行的。
交互式 shell 进程
如果bash是作为交互式shell启动的,它就不会访问/etc/profile文件,只会检查用户HOME目录中的.bashrc文件。
.bashrc文件有两个作用:一是查看/etc目录下通用的bashrc文件,二是为用户提供一个定制自
己的命令别名和私有脚本函数的地方。
非交互式 shell
最后一种shell是非交互式shell。系统执行shell脚本时用的就是这种shell。不同的地方在于它没有命令行提示符。但是当你在系统上运行脚本时,也许希望能够运行一些特定启动的命令。
为了处理这种情况,bash shell提供了BASH_ENV环境变量。当shell启动一个非交互式shell进程时,它会检查这个环境变量来查看要执行的启动文件。如果有指定的文件,shell会执行该文件里的命令,这通常包括shell脚本变量设置。
环境变量持久化
对全局环境变量来说(Linux系统中所有用户都需要使用的变量),可能更倾向于将新的或修改过的变量设置放在/etc/profile文件中,但这可不是什么好主意。如果你升级了所用的发行版,这个文件也会跟着更新,那你所有定制过的变量设置可就都没有了。
最好是在/etc/profile.d目录中创建一个以.sh结尾的文件。把所有新的或修改过的全局环境变量设置放在这个文件中。
在大多数发行版中,存储个人用户永久性bash shell变量的地方是$HOME/.bashrc文件。这一点适用于所有类型的shell进程。但如果设置了BASH_ENV变量,那么记住,除非它指向的是$HOME/.bashrc,否则你应该将非交互式shell的用户变量放在别的地方。
数组变量
环境变量有一个很酷的特性就是,它们可作为数组使用。数组是能够存储多个值的变量。这些值可以单独引用,也可以作为整个数组来引用。
要给某个环境变量设置多个值,可以把值放在括号里,值与值之间用空格分隔。
$ mytest=(one two three four five)
没什么特别的地方。如果你想把数组像普通的环境变量那样显示,你会失望的。
$ echo $mytest
one
只有数组的第一个值显示出来了。要引用一个单独的数组元素,就必须用代表它在数组中位置的数值索引值。索引值要用方括号括起来。
$ echo ${mytest[2]}
three
要显示整个数组变量,可用星号作为通配符放在索引值的位置。
$ echo ${mytest[*]}
one two three four five
也可以改变某个索引值位置的值。
$ mytest[2]=seven
$
$ echo ${mytest[*]}
one two seven four five
甚至能用unset命令删除数组中的某个值,但是要小心,这可能会有点复杂。看下面的例子。
$ unset mytest[2]
$
$ echo ${mytest[*]}
one two four five
$
$ echo ${mytest[2]}
$ echo ${mytest[3]}
four
这个例子用unset命令删除在索引值为2的位置上的值。显示整个数组时,看起来像是索引里面已经没这个索引了。但当专门显示索引值为2的位置上的值时,就能看到这个位置是空的。
最后,可以在unset命令后跟上数组名来删除整个数组。
$ unset mytest
$
$ echo ${mytest[*]}
有时数组变量会让事情很麻烦,所以在shell脚本编程时并不常用。对其他shell而言,数组变量的可移植性并不好,如果需要在不同的shell环境下从事大量的脚本编写工作,这会带来很多不便。有些bash系统环境变量使用了数组(比如BASH_VERSINFO),但总体上不会太频繁用。