环境变量在Linux系统中的用途很多。你现在已经知道如何修改系统环境变量,也知道了如
何创建自己的环境变量。接下来的问题是怎样让环境变量的作用持久化。
在你登入Linux系统启动一个bash shell时,默认情况下bash会在几个文件中查找命令。这些
文件叫作启动文件或环境文件。bash检查的启动文件取决于你启动bash shell的方式。启动bash
shell有3种方式:
- 登录时作为默认登录shell
- 作为非登录shell的交互式shell
- 作为运行脚本的非交互shell
下面几节介绍了bash shell在不同的方式下启动文件。
6.6.1 登录 shell
当你登录Linux系统时,bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里
读取命令:
- /etc/profile
- $HOME/.bash_profile
- $HOME/.bashrc
- $HOME/.bash_login
- $HOME/.profile
/etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行
这个启动文件。
说明 要留意的是有些Linux发行版使用了可拆卸式认证模块(Pluggable Authentication
Modules ,PAM)。在这种情况下,PAM文件会在bash shell启动之前处理,这些文件中可
能会包含环境变量。PAM文件包括/etc/environment文件和$HOME/.pam_environment文件。
PAM更多的相关信息可以在http://linux-pam.org中找到。
另外4个启动文件是针对用户的,可根据个人需求定制。我们来仔细看一下各个文件。
1. /etc/profile文件
/etc/profile文件是bash shell默认的的主启动文件。只要你登录了Linux系统,bash就会执行
/etc/profile启动文件中的命令。不同的Linux发行版在这个文件里放了不同的命令。在本书所用的
Ubuntu Linux系统上,它看起来是这样的:
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
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
# The default umask is now handled by pam_umask.
# See pam_umask(8) and /etc/login.defs.
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
这个文件中的大部分命令和语法都会在第12章以及后续章节中具体讲到。每个发行版的
/etc/profile文件都有不同的设置和命令。例如,在上面所显示的Ubuntu发行版的/etc/profile文件中,
涉及了一个叫作/etc/bash.bashrc的文件。这个文件包含了系统环境变量。
但是,在下面显示的CentOS发行版的/etc/profile文件中,并没有出现这个文件。另外要注意
的是,该发行版的/etc/profile文件还在内部导出了一些系统环境变量。
# /etc/profile
# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc
# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, to
# prevent the need for merging in future updates.
pathmunge () {
case ":${PATH}:" in
*:"$1":*)
;;
*)
if [ "$2" = "after" ] ; then
PATH=$PATH:$1
else
PATH=$1:$PATH
fi
esac
}
if [ -x /usr/bin/id ]; then
if [ -z "$EUID" ]; then
# ksh workaround
EUID=`id -u`
UID=`id -ru`
fi
USER="`id -un`"
LOGNAME=$USER
MAIL="/var/spool/mail/$USER"
fi
# Path manipulation
if [ "$EUID" = "0" ]; then
pathmunge /sbin
pathmunge /usr/sbin
pathmunge /usr/local/sbin
else
pathmunge /usr/local/sbin after
pathmunge /usr/sbin after
pathmunge /sbin after
fi
HOSTNAME=`/bin/hostname 2>/dev/null`
HISTSIZE=1000
if [ "$HISTCONTROL" = "ignorespace" ] ; then
export HISTCONTROL=ignoreboth
else
export HISTCONTROL=ignoredups
fi
export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL
# By default, we want umask to get set. This sets it for login shell
# Current threshold for system reserved uid/gids is 200
# You could check uidgid reservation validity in
# /usr/share/doc/setup-*/uidgid file
if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
umask 002
else
umask 022
fi
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
unset i
unset -f pathmunge
这两个发行版的/etc/profile文件都用到了同一个特性:for语句。它用来迭代/etc/profile.d目录下的所有文件。(该语句会在第13章中详述。)这为Linux系统提供了一个放置特定应用程序启
动文件的地方,当用户登录时,shell会执行这些文件。在本书所用的Ubuntu Linux系统中,
/etc/profile.d目录下包含以下文件:
$ ls -l /etc/profile.d
total 12
-rw-r—r— 1 root root 40 Apr 15 06:26 appmenu-qt5.sh
-rw-r—r— 1 root root 663 Apr 7 10:10 bash_completion.sh
-rw-r—r— 1 root root 1947 Nov 22 2013 vte.sh
$
在CentOS系统中,/etc/profile.d目录下的文件更多: $ ls -l /etc/profile.d
total 80
-rw-r—r—. 1 root root 1127 Mar 5 07:17 colorls.csh
-rw-r—r—. 1 root root 1143 Mar 5 07:17 colorls.sh
-rw-r—r—. 1 root root 92 Nov 22 2013 cvs.csh
-rw-r—r—. 1 root root 78 Nov 22 2013 cvs.sh
-rw-r—r—. 1 root root 192 Feb 24 09:24 glib2.csh
-rw-r—r—. 1 root root 192 Feb 24 09:24 glib2.sh
-rw-r—r—. 1 root root 58 Nov 22 2013 gnome-ssh-askpass.csh
-rw-r—r—. 1 root root 70 Nov 22 2013 gnome-ssh-askpass.sh
-rwxr-xr-x. 1 root root 373 Sep 23 2009 kde.csh
-rwxr-xr-x. 1 root root 288 Sep 23 2009 kde.sh
-rw-r—r—. 1 root root 1741 Feb 20 05:44 lang.csh
-rw-r—r—. 1 root root 2706 Feb 20 05:44 lang.sh
-rw-r—r—. 1 root root 122 Feb 7 2007 less.csh
-rw-r—r—. 1 root root 108 Feb 7 2007 less.sh
-rw-r—r—. 1 root root 976 Sep 23 2011 qt.csh
-rw-r—r—. 1 root root 912 Sep 23 2011 qt.sh
-rw-r—r—. 1 root root 2142 Mar 13 15:37 udisks-bash-completion.sh
-rw-r—r—. 1 root root 97 Apr 5 2012 vim.csh
-rw-r—r—. 1 root root 269 Apr 5 2012 vim.sh-rw-r—r—. 1 root root 169 May 20 2009 which2.sh
$
不难发现,有些文件与系统中的特定应用有关。大部分应用都会创建两个启动文件:一个供
bash shell使用(使用.sh扩展名),一个供c shell使用(使用.csh扩展名)。
lang.csh和lang.sh文件会尝试去判定系统上所采用的默认语言字符集,然后设置对应的LANG
环境变量。
2. $HOME目录下的启动文件
剩下的启动文件都起着同一个作用:提供一个用户专属的启动文件来定义该用户所用到的环
境变量。大多数Linux发行版只用这四个启动文件中的一到两个:
- $HOME/.bash_profile
- $HOME/.bashrc
- $HOME/.bash_login
- $HOME/.profile
注意,这四个文件都以点号开头,这说明它们是隐藏文件(不会在通常的ls命令输出列表中
出现)。它们位于用户的HOME目录下,所以每个用户都可以编辑这些文件并添加自己的环境变
量,这些环境变量会在每次启动bash shell会话时生效。
说明 Linux发行版在环境文件方面存在的差异非常大。本节中所列出的$HOME下的那些文件并
非每个用户都有。例如有些用户可能只有一个$HOME/.bash_profile文件。这很正常。
shell会按照按照下列顺序,运行第一个被找到的文件,余下的则被忽略:
$HOME/.bash_profile
$HOME/.bash_login
$HOME/.profile
注意,这个列表中并没有$HOME/.bashrc文件。这是因为该文件通常通过其他文件运行的。
窍门 记住,$HOME表示的是某个用户的主目录。它和波浪号(~)的作用一样。
CentOS Linux系统中的.bash_profile文件的内容如下:
cat $HOME/.bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
.bash_profile启动文件会先去检查HOME目录中是不是还有一个叫.bashrc的启动文件。如果有
的话,会先执行启动文件里面的命令。
6.6.2 交互式 shell 进程
如果你的bash shell不是登录系统时启动的(比如是在命令行提示符下敲入bash时启动),那
么你启动的shell叫作交互式shell。交互式shell不会像登录shell一样运行,但它依然提供了命令行
提示符来输入命令。
如果bash是作为交互式shell启动的,它就不会访问/etc/profile文件,只会检查用户HOME目录
中的.bashrc文件。
在本书所用的CentOS Linux系统上,这个文件看起来如下:
cat .bashrc
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# User specific aliases and functions
bashrc文件有两个作用:一是查看/etc目录下通用的bashrc文件,二是为用户提供一个定制自
己的命令别名(参见第5章)和私有脚本函数(将在第17章中讲到)的地方。
6.6.3 非交互式 shell
最后一种shell是非交互式shell。系统执行shell脚本时用的就是这种shell。不同的地方在于它
没有命令行提示符。但是当你在系统上运行脚本时,也许希望能够运行一些特定启动的命令。
窍门 脚本能以不同的方式执行。只有其中的某一些方式能够启动子shell。你会在第11章中学习
到shell不同的执行方式。
为了处理这种情况,bash shell提供了BASH_ENV环境变量。当shell启动一个非交互式shell进
程时,它会检查这个环境变量来查看要执行的启动文件。如果有指定的文件,shell会执行该文件
里的命令,这通常包括shell脚本变量设置。
在本书所用的CentOS Linux发行版中,这个环境变量在默认情况下并未设置。如果变量未设
置,printenv命令只会返回CLI提示符:
printenv BASH_ENV
在本书所用的Ubuntu发行版中,变量BASH_ENV也没有被设置。记住,如果变量未设置,echo
命令会显示一个空行,然后返回CLI提示符:
echo $BASH_ENV
那如果BASH_ENV变量没有设置,shell脚本到哪里去获得它们的环境变量呢?别忘了有些
shell脚本是通过启动一个子shell来执行的(参见第5章)。子shell可以继承父shell导出过的变量。
举例来说,如果父shell是登录shell,在/etc/profile、/etc/profile.d/*.sh和$HOME/.bashrc文件中
设置并导出了变量,用于执行脚本的子shell就能够继承这些变量。
要记住,由父shell设置但并未导出的变量都是局部变量。子shell无法继承局部变量。
对于那些不启动子shell的脚本,变量已经存在于当前shell中了。所以就算没有设置
BASH_ENV,也可以使用当前shell的局部变量和全局变量。
6.6.4 环境变量持久化
现在你已经了解了各种shell进程以及对应的环境文件,找出永久性环境变量就容易多了。也
可以利用这些文件创建自己的永久性全局变量或局部变量。
对全局环境变量来说(Linux系统中所有用户都需要使用的变量),可能更倾向于将新的或修
改过的变量设置放在/etc/profile文件中,但这可不是什么好主意。如果你升级了所用的发行版,
这个文件也会跟着更新,那你所有定制过的变量设置可就都没有了。
最好是在/etc/profile.d目录中创建一个以.sh结尾的文件。把所有新的或修改过的全局环境变
量设置放在这个文件中。
在大多数发行版中,存储个人用户永久性bash shell变量的地方是$HOME/.bashrc文件。这一
点适用于所有类型的shell进程。但如果设置了BASH_ENV变量,那么记住,除非它指向的是
$HOME/.bashrc,否则你应该将非交互式shell的用户变量放在别的地方。
说明 图形化界面组成部分(如GUI客户端)的环境变量可能需要在另外一些配置文件中设置,
这和设置bash shell环境变量的地方不一样。
想想第5章中讲过的alias命令设置就是不能持久的。你可以把自己的alias设置放在
$HOME/.bashrc启动文件中,使其效果永久化。