本文主要参考《Linux命令行与shell脚本编程大全》,说的不好听就是把书上的内容复制到这里,便于理解和后续查看。
很多程序和脚本都通过环境变量来获取系统信息、存储临时数据和配置信息,在Linux系统上有很多地方可以设置环境变量。
什么是环境变量
bash shell 用一个称作环境变量的特性来存储有关shell会话和工作环境的信息。它允许在内存中存储数据,以便运行在shell上的程序和脚本访问。这也是存储永久数据的一种便捷方法,这些数据可以用来识别用户账户、系统、shell的特性以及任何其他你需要的数据。
bash shell中,环境变量分为两类:
全局变量
局部变量
全局环境变量
全局环境变量不仅对shell会话可见,对有关shell创建的子进程也可见。局部环境变量则只对创建它们的shell可见。(问题:不是当前shell创建的全局环境变量是否可见?答案:不可见,重新打开一个shell窗口后,全局环境变量也丢失了。)
Linux系统在你开始使用shell前就设置了一些全局环境变量,系统环境变量一般全大写字母以区别普通用户的环境变量。
查看全局环境变量可用printenv
命令:
$ printenv
XDG_VTNR=2
XDG_SESSION_ID=1
HOSTNAME=luck
VTE_VERSION=4205
TERM=xterm-256color
......
如你所见,系统为bash shell设置了很多全局环境变量,它们大部分是系统在用户登陆时设置的。
要显示单个环境变量的值,可用echo
命令。当引用环境变量时,必须在环境变量的名称前放一个$
符:
echo $HOME
/home/tuanjie
局部环境变量
顾名思义,局部环境变量只能在定义它们的进程中可见。尽管它们是局部的,却和全局环境变量一样重要。事实上,Linux系统也默认定义了标准局部环境变量。
Linux系统并没有一个只显示局部环境变量的命令。set
命令会显示谟阁特定进程设置的所有环境变量。当然,这也包括全局环境变量。
下面是set
输出的示例:
set
BASH=/usr/bin/bash
BASHOPTS=checkwinsize:cmdhist:complete_fullquote:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_COMPLETION_COMPAT_DIR=/etc/bash_completion.d
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="4" [1]="3" [2]="42" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.3.42(1)-release'
COLUMNS=80
......
可以看到,所有通过printenv
能看到的全局环境变量都出现在set
命令的输出中。但set命令的输出中还有一些其他的环境变量,这些就是局部环境变量。
设置环境变量
你可以在bash shell中设置自己的环境变量。本节将介绍怎样在交互式shell或shell脚本程序中创建自己的环境变量并引用它们。
设置局部环境变量
一旦启动bash shell或者执行了shell脚本,你就能创建在这个shell进程中可见的局部环境变量了。你可以通过等号来给环境变量赋值,值可以是数值或者字符串:
test=testing
echo $test
testing
如果要给变量赋一个带有空格的字符串,必须用单引号来界定字符串的开始和末尾:
test=test a long string
bash: a: 未找到命令...
文件搜索失败: Curl error (56): Failure when receiving data from the peer for https://www.virtualbox.org/download/oracle_vbox.asc [Proxy CONNECT aborted]
test='test a long string'
echo $test
test a long string
没有单引号的话,bash shell会以为下一个字符串是一个要执行的命令。注意,你定义的局部环境变量用的是小写字母,而到目前为止你所看到的系统环境变量都是大写字母。
这三bash shell的标准惯例。创建新的环境变量时,推荐你用小写字母。它能帮你区分用户个人环境变量和系统环境变量。
设置了局部环境变量后,就能在shell的任何地方使用它了。但是,如果创建了另外一个shell,它在子shell中就不能用了。
设置全局环境变量
全局环境变量在设定该全局环境变量的进程创建的子进程中都是可见的。创建全局环境变量的方法是县创建一个局部环境变量,然后再把它导出到全局环境中。
这个过程通过export
命令来完成:
echo $test
testing a long string
export test
bash
echo $test
testing a long string
导出局部环境变量test
后,我们启动了子shell进程并在子shell中查看了test
环境变量的值。这次,因为export
命令让它变成了全局的,环境变量保持了它的值。
删除环境变量
可以使用unset
命令来删除环境变量:
echo $test
testing a long string
unset test
echo $test
在处理全局环境变量时,事情就有点复杂了。如果你在子进程中删除了一个全局环境变量,它只对子进程有效。该环境变量在父进程中依然有效。
默认shell环境变量
默认情况下,bash shell会用一些特定的环境变量来定义系统环境。
比如:
PATH 冒号分隔的shell查找命令的目录列表
在shell命令行界面输入命令时,shell必须在系统中查找程序。在我的Linux系统上,PATH环境变量时这样的:
echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/tuanjie/geode/bin:/home/tuanjie/.local/bin:/home/tuanjie/bin
shell将在这几个目录中查找命令。PATH中的每个目录都由冒号分隔。在PAHT的末尾没有任何特殊符合表示这是目录列表的结尾。你可以通过在PATH变量的末尾加个冒号再加新的目录来添加其他目录。PATH变量同时显示了shell查找命令的顺序。
另外需要注意的是,不是所有默认环境变量都会在运行set命令时列出,也不是它们所有都必须有一个值。
设置PAHT环境变量
PATH环境变量时Linux上造成最多问题的变量。它定义了命令行输入命令的搜索庐江。如果找不到命令,它会产生一个错误:
myprog
myprog: 未找到命令...
文件搜索失败: Curl error (56): Failure when receiving data from the peer for https://www.virtualbox.org/download/oracle_vbox.asc [Proxy CONNECT aborted]
问题是通常应用会把可执行程序放到不再PAHT环境变量中的目录。解决方法时保证PATH环境变量包含了所有存放应用的目录。
你可以添加新的搜索目录到现有的PATH环境变量,无需从头定义。PATH中的目录是用冒号分隔的,你只需要引用原来的PATH值,然后再给字符串添加新目录就行了。可以参考下面的例子:
PATH=$PATH:/home/user/test
程序员通常的做法是把单点符也加入到PATH环境变量中。这个单点符代表当前目录。
下节,你将了解到如何修改环境变量使其一直在你的系统上,这样你就能一直执行你的程序了。
定位系统环境变量
Linux系统用环境变量来在程序和脚本中标识它自己。这为你的程序提供了获得系统信息的一个简便方法。问题是如何设置这些变量。
在你登陆Linux系统启动一个bash shell时,默认情况下bash在几个文件中查找命令。这些文件称作启动文件。bash检查的启动文件取决于你启动bash shell的方式。启动bash shell有3种方式:
登录时当作默认登录shell
作为非登录shell的交互式shell
作为运行脚本的非交互式shell
下面几节介绍了bash shell在不同的启动方式下检查的启动文件。
登陆shell
当你登录Linux系统时,bash shell会作为登录shell启动。登录shell会从4个不同的启动文件里读取命令。下面时bash shell处理这些文件的顺序:
/etc/profile
$HOME/.bash_profile
$HOME/.bash_login
$HOME/.profile
/etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件。另外3个启动文件是用户专有的,所以可以根据每个用户的具体需求定制。我们来仔细看下各个文件。
1./etc/profile
/etc/profile文件是bash shell的主启动文件。只要你登录了Linux系统,bash就会执行/etc/profile文件中的命令。不同的发行版在这个文件里放了不同的命令。在这个Linux系统上,它看起来是这样的:
cat /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, as this
# will 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 /usr/sbin
pathmunge /usr/local/sbin
else
pathmunge /usr/local/sbin after
pathmunge /usr/sbin after
fi
HOSTNAME=`/usr/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
PATH=$PATH:/home/tuanjie/geode/bin
export PATH
# 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
fi
fi
done
unset i
unset -f pathmunge
这个文件中你能看到的大部分命令和脚本都会在第10章讲到。现在重要的是留意一下这个文件中设置的环境变量。看一下底部文件的导出行:
export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL
这保证了这些环境变量对这个登录shell创建的子进程都有效。
profile文件还有一个复杂的特性。它有个for语句,会逐一访问位于/etc/profile.d目录的每个文件(for语句我们会在第12章讨论)。它为Linux系统提供了一个集中存放用户登录时要执行的应用专属的启动文件的地方。在这个Linux上,profile.d目录下有如下文件:
ls -l /etc/profile.d/
总用量 88
-rw-r--r--. 1 root root 771 12月 7 2015 256term.csh
-rw-r--r--. 1 root root 841 12月 7 2015 256term.sh
-rw-r--r--. 1 root root 666 6月 17 2015 bash_completion.sh
-rw-r--r--. 1 root root 196 1月 12 17:51 colorgrep.csh
-rw-r--r--. 1 root root 201 1月 12 17:51 colorgrep.sh
-rw-r--r--. 1 root root 1741 5月 19 23:13 colorls.csh
-rw-r--r--. 1 root root 1609 5月 19 23:13 colorls.sh
-rw-r--r--. 1 root root 162 7月 9 2015 colorxzgrep.csh
-rw-r--r--. 1 root root 183 7月 9 2015 colorxzgrep.sh
-rw-r--r--. 1 root root 216 7月 10 2015 colorzgrep.csh
-rw-r--r--. 1 root root 220 7月 10 2015 colorzgrep.sh
-rw-r--r--. 1 root root 58 4月 22 21:57 gnome-ssh-askpass.csh
-rw-r--r--. 1 root root 70 4月 22 21:57 gnome-ssh-askpass.sh
-rw-r--r--. 1 root root 1706 12月 7 2015 lang.csh
-rw-r--r--. 1 root root 2703 12月 7 2015 lang.sh
-rw-r--r--. 1 root root 500 4月 25 13:05 less.csh
-rw-r--r--. 1 root root 253 4月 25 13:05 less.sh
lrwxrwxrwx. 1 root root 29 10月 30 2015 modules.csh -> /etc/alternatives/modules.csh
lrwxrwxrwx. 1 root root 28 10月 30 2015 modules.sh -> /etc/alternatives/modules.sh
-rw-r--r--. 1 root root 1157 11月 27 2015 PackageKit.sh
-rw-r--r--. 1 root root 663 1月 20 2015 scl-init.sh
-rw-r--r--. 1 root root 2092 3月 15 23:53 vte.sh
-rw-r--r--. 1 root root 164 6月 19 2015 which2.csh
-rw-r--r--. 1 root root 200 6月 19 2015 which2.sh
不难发现,这些基本上都跟系统上的特定应用有关。大部分应用会创建两个启动文件:一个给bash shell,一个给c shell。
2.$HOME目录下的启动文件
剩下的3个启动文件都起着同一个作用:提供一个用户专属的启动文件来定义用户专有的环境变量。
略。
交互式shell
略
非交互式shell
略
可变数组
略
使用命令别名
略
小结
略
bash shell会在启动时执行几个启动文件。这些启动文件里有一些环境变量定义,他们会在每个bash 会话定义标准环境变量。每次登录Linux系统 时,bash shell都会访问/etc/profile启动文件,然后访问3个针对每个用户的本地启动文件。用户在这些文件中定制自己想要的环境变量和启动脚本。
略