什么是环境变量
环境变量(environment variable)用于存储有关shell会话和工作环境的信息,以便程序或脚本能够轻松访问它们。环境变量有两类:
- 全局变量
- 局部变量
全局变量对shell和子shell都可见,而局部变量只对创建它们的shell可见。
全局环境变量
Linux 会在开始 bash 会话时设置一些全局环境变量,系统环境变量基本上全是大写字母,以区别普通用户环境变量。
我们可以用 env
或 printenv
命令:
$ env
SHELL=/bin/bash
LANGUAGE=en_US:en
PWD=/home/pi
LOGNAME=pi
XDG_SESSION_TYPE=tty
HOME=/home/pi
LANG=en_US.UTF-8
[...]
上面只列出了部分系统环境变量。我们可以用 printenv
来查看特定环境变量的值:
$ printenv HOME
/home/pi
或者用 echo
也行,不过需要在环境变量前加 $
$ echo $HOME
/home/pi
在环境变量前加 $
实际上是取值,echo
则类似于 c 语言中的 printf。利用这个我们可以用变量来代替命令参数:
$ ls $HOME
Desktop Downloads Music Public
全局变量可以用于进程的所有子shell:
$ bash
$ ps --forest
PID TTY TIME CMD
32424 pts/3 00:00:00 bash
665 pts/3 00:00:00 \_ bash
764 pts/3 00:00:00 \_ ps
$ echo $HOME
/home/pi
$ exit
exit
局部环境变量
Linux 并没有一个只显示局部环境变量的命令,set
命令会显示所有环境变量,包括局部、全局、用户自定义变量:
$ set
BASH=/bin/bash
[...]
设置用户定义变量
设置局部用户定义变量
为了区分系统变量,我们要求用户定义变量必须要小写。设置局部变量时无需声明,直接用等号赋值即可:
$ my_variable=Hello
$ echo $my_variable
Hello
赋值可以是数字或字符串,如果字符串中有空格,则需要用引号围起来(单、双均可):
$ my_variable='Hello World'
$ echo $my_variable
Hello World
$ my_variable="Hello World"
$ echo $my_variable
Hello World
另外要注意的是,变量名、等号、值之间没有空格。如果加了空格,bash shell 就会把变量当成一个单独的命令:
$ my_variable = "Hello World"
-bash: my_variable: command not found
父 shell 局部变量不能在子 shell 中使用,同理子 shell 的局部变量也不能在父 shell 中使用:
$ my_variable="Hello World"
$ bash
$ echo $my_variable
设置全局用户定义变量
创建全局变量的方法是先创建局部变量,然后再用 export
导出到全局环境中:
$ my_variable="I am Global"
$ export my_variable
$ bash
$ echo $my_variable
I am Global
如果在子 shell 中修改全局变量,并不会影响父 shell 中该变量的值:
#接上面
$ my_variable="NULL"
$ echo $my_variable
NULL
$ exit
exit
$ echo $my_variable
I am Global
删除环境变量
可以用 unset
删除环境变量,注意不要加 $
:
$ echo $my_variable
I am Global
$ unset my_variable
$ echo $my_variable
怎么判断是否加 $ 呢?如果要用到变量,就加;如果要操作变量,就不加。不过有个例外就是用 printenv 显示变量的值时不用加。
另外,如果在子 shell 中删除了全局环境变量,则只对子 shell 有效。该变量在父 shell 中依然可用。
设置 PATH 环境变量
当输入外部命令时,shell 必须搜索系统来找到对应的程序,PATH 环境变量就定义了进行命令和程序查找的目录:
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/snap/bin
PATH 中的目录用冒号分隔。我们可以添加新的目录:
$ PATH=$PATH:/home/pi
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/snap/bin:/home/pi
程序员比较常用的是将单点符也加入环境变量,单点符表示当前目录:
$ PATH=$PAHT:.
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/snap/bin:/home/pi:.
但这种修改只对当前 shell 有效,退出或重启后就会恢复默认。下一节会讲如何永久保持环境变量的修改效果。
定位系统环境变量
当我们启动一个 shell 时,bash 会通过运行一些文件来导入环境变量。这些文件叫做 启动文件 或 环境文件。根据我们启动 shell 的方式,启动文件会有所不同。比如:
- 登录时作为默认登录 shell
- /etc/profile
- $HOME/.bash_profile
- $HOME/.bashrc
- $HOME/.bash_login
- $HOME/.profile
- 作为非登录 shell 的交互式 shell
- $HOME/.bashrc
- 作为运行脚本的非交互 shell
- $BASH_ENV
下面我们来一个一个分析。
登录 shell
/etc/profile 是 bash shell 默认的主启动文件,只要登录了 Linux 系统,bash 就会执行该文件中的命令。不同 Linux 发行版的命令不同,比如在 Debian 或 Ubuntu 上,有如下命令:
$ cat /etc/profile
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
if [ "`id -u`" -eq 0 ]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
fi
export PATH
if [ "${PS1-}" ]; then
if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then
# The file bash.bashrc already sets the default PS1.
# PS1='\h:\w\$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "`id -u`" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
可以看出命令中涉及了 /etc/bash.bashec
的文件,这个文件包含了系统环境变量。
然后 shell 会按照下面顺序,运行第一个被找到的文件,余下的则被忽略:
- $HOME/.bash_profile
- $HOME/.bash_login
- $HOME/.profile
这个列表中没有 $HOME/.bashrc,因为这个文件通常通过其他文件运行的。比如我的 $HOME/.profile 是这样的:
$ cat .profile
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.
# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi
交互式 shell
如果 bash shell 不是登录系统时启动的(比如在命令提示符敲入 bash 时启动),这种 shell 就叫 交互式 shell,它只会检查 $HOME/.bashrc 文件(但会继承父 shell 的全局变量)。
$HOME/.bashrc 有两个作用:
- 用于查看 /etc 目录下通用的 bashrc 文件
- 为用户提供一个定制自己目录别名和私有脚本函数的地方
非交互式 shell
系统执行 shell 脚本时会启动非交互 shell。它会检查 BASH_ENV 环境变量来查看要执行的启动文件。
持久化环境变量
知道了系统是如何导入环境变量后,我们可以在对应的文件中添加自己的环境变量。在大多数发行版中,存储个人用户永久的 bash shell 变量的地方是 $HOME/.bashrc 文件。
GUI 客户端的环境变量可能要在另外一些配置文件中设置
数组变量
环境变量可以作为数组使用,只需要把多个值放在括号里,值与值之间用空格分隔:
$ echo $my_list
zero
$ echo ${my_list[2]}
two
$ echo $my_list[2]
zero[2]
要显示整个数组变量,可以用 *
作为通配符放在索引的位置:
$ echo ${my_list[*]}
zero one two
可以单独改变某个索引位置的值:
$ my_list[2]=2
$ echo ${my_list[*]}
zero one 2
甚至可以用 unset
删除某个位置的值,但注意,这不会改变其他元素的索引:
$ unset my_list[1]
$ echo ${my_list[*]}
zero 2
$ echo ${my_list[1]}
$ echo ${my_list[2]}
2
也可以用 unset
删除整个数组:
$ unset my_list
$ echo ${my_list[*]}