您的位置:首页 > 运维架构 > Linux

使用Linux环境变量——第六章

2017-11-19 00:00 302 查看
摘要: 《Linux命令行与shell脚本编程大全》使用Linux环境变量——第六章

环境变量

6.1 什么是环境变量

Bash shell用一个叫作环境变量的特性来存储有关shell 会话和工作环境的信息。

在bash shell中,环境变量分为两类:

全局变量

局部变量

本节将描述以上环境变量并演示怎么查看和使用它们。

全局环境变量对于shell会话和所有生成的子shell都是可见的。局部变量则只针对创建他们的shell可见。

Linux 系统在你开始bash会话时就设置了一些全局环境变量,系统环境变量基本上都是用大写字母,以区别于普通用户的环境变量。

6.1.1 全局环境变量

查看全局变量使用env 或者 printenv命令

[root@centos1 ~]# printenv
HOSTNAME=centos1
SELINUX_ROLE_REQUESTED=
TERM=xterm
SHELL=/bin/bash
HADOOP_HOME=/home/hadoop
HISTSIZE=1000
SSH_CLIENT=192.168.20.110 53812 22
SELINUX_USE_CURRENT_RANGE=
QTDIR=/usr/lib64/qt-3.3
QTINC=/usr/lib64/qt-3.3/include
SSH_TTY=/dev/pts/0
...
[root@centos1 ~]# env
HOSTNAME=centos1
SELINUX_ROLE_REQUESTED=
TERM=xterm
SHELL=/bin/bash
HADOOP_HOME=/home/hadoop
HISTSIZE=1000
SSH_CLIENT=192.168.20.110 53812 22
SELINUX_USE_CURRENT_RANGE=
QTDIR=/usr/lib64/qt-3.3
QTINC=/usr/lib64/qt-3.3/include
SSH_TTY=/dev/pts/0
...

登录方式也会影响到所设置的环境变量

[root@centos1 ~]# printenv HOME
/root
[root@centos1 ~]# env HOME
env: HOME: No such file or directory
[root@centos1 ~]# echo $HOME
/root
[root@centos1 ~]# #

要显示个别环境变量的值,可以使用printenv命令,但是不要使用env命令。

也可以使用echo显示环境变量额值。在这种情况下引用某个环境变量的时候必须在变量前面加上美元符($)。

在echo 命令中,在变量名钱加上$可不仅仅是要显示变量当前的值。它能够让变量作为命令行参数。

[root@centos1 ~]# ls $HOME
anaconda-ks.cfg  Documents  hadoop-2.7.1.tar.gz  install.log.syslog  Pictures  Templates
Desktop          Downloads  install.log          Music               Public    Videos

正如前面提到的,全局变量可作用于进程的所有子shell。

[root@centos1 ~]# bash
[root@centos1 ~]# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root      2317  2313  0 22:51 pts/0    00:00:00 -bash
root      2351  2317  0 22:57 pts/0    00:00:00 bash
root      2360  2351  9 22:57 pts/0    00:00:00 ps -f
[root@centos1 ~]# echo $HOME
/root
[root@centos1 ~]# exit
exit
[root@centos1 ~]#


6.1.2 局部环境变量

局部环境变量只能在定义它们的进程中可见。实际上,Linux系统默认也定义了标准的局部环境变量。不过你也可以定义自己的局部变量,这些变量成为用户定义局部变量

查看局部环境变量的列表有点复杂,没有一个只显示局部环境变量的命令。set 命令会显示某个特定进程设置的所有环境变量,包括局部变量全局变量,以及用户定义变量

[root@centos1 ~]# set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
...


env、printenv和set 之间差异很细微。set命令会显示出全局变量、局部变量及用户自定义变量,它还会按照字母顺序对结果进行排序。env、printenv 命令与set命令区别在于前两个命令不会对变量排序,也不会输出局部变量及用户自定义变量。

6.2 设置用户定义变量

本节将介绍在交互式shell中或者shell脚本中创建自己的变量并引用它们。

6.2.1 设置局部用户定义变量

一旦启动一个bash shell(或者执行一个shell脚本),就能创建在这个shell进程内可见的局部变量。可以通过等号给环境变量赋值,值可以是数值或字符串。

[root@centos1 ~]# echo $my_variable

[root@centos1 ~]# my_variable=HELLO
[root@centos1 ~]# echo $my_variable
HELLO

设置用户自定义变量非常简单,如果给变量赋值一个含有空格的字符串值,必须用引号界定字符串的首和尾。

[root@centos1 ~]# echo $my_variable
HELLO
[root@centos1 ~]# my_variable="Hello World"
[root@centos1 ~]# echo $my_variable
Hello World
[root@centos1 ~]# my_variable='Hello World'
[root@centos1 ~]# echo $my_variable
Hello World

如果没有引号的话,bash shell 会以为下一个词是一个要执行的命令。

敲门:所有环境变量名均使用大写字母,这是bash shell 的标准惯例,如果是你自己创建的局部变量或是shell脚本,请使用小写字母。变量名区分大小写。在涉及用户自定义的局部变量时坚持使用小写字母,这能够避免重新定义系统环境变量可能带来的灾难。

记住,变量名、等号和值之间没有空格,这一点非常重要,如果在赋值表达式中加上了空格 bash shell就会把值当成一个单独的命令。

设置了局部环境变量后,就能在shell进程的任何地方使用它了。但是如果生成了另外一个shell ,它在子shell中就不可用。

[root@centos1 ~]# my_variable='Hello World'
[root@centos1 ~]# echo $my_variable
Hello World
[root@centos1 ~]# bash
[root@centos1 ~]# echo $my_variable

[root@centos1 ~]# exit
exit
[root@centos1 ~]# echo $my_variable
Hello World
[root@centos1 ~]#

当你退出了子进程,那个局部环境变量就依然可用。

类似的,如果在子进程中设置了一个局部变量,那么一旦你退出了子进程,那个局部环境变量就不能用了。

[root@centos1 ~]# echo $my_child

[root@centos1 ~]# bash
[root@centos1 ~]# my_child="Hello Little World"
[root@centos1 ~]# echo $my_child
Hello Little World
[root@centos1 ~]# exit
exit
[root@centos1 ~]# echo $my_child

当我们回到父shell时,子shell 中设置的局部变量就不存在了。可以通过将局部的用户定义变量编程全局变量来改变这种情况。

6.2.2 设置全局环境变量

在设置全局环境变量的进程锁创建的子进程中,该变量都是可见的。创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局环境中。

这个过程可以通过export 命令来完成,变量名前不需加 $

[root@centos1 ~]# echo $my_variable
Hello World
[root@centos1 ~]# export my_variable
[root@centos1 ~]# echo $my_variable
Hello World
[root@centos1 ~]# bash
[root@centos1 ~]# echo $my_variable
Hello World
[root@centos1 ~]# exit
exit
[root@centos1 ~]# echo $my_variable
Hello World
[root@centos1 ~]#

定义并导入局部环境变量my_variable 后,bash命令启动了一个子shell,在这个子shell中能够显示出变量my_variable的值。该变量能够保留它的值是应为export命令使其变成了全局变量。

修改子shell中的全局变量并不会影响到父shell中该变量的值。

[root@centos1 ~]# my_variable="I an Global now"
[root@centos1 ~]# export my_variable
[root@centos1 ~]# echo $my_variable
I an Global now
[root@centos1 ~]# bash
[root@centos1 ~]# echo $my_variable
I an Global now
[root@centos1 ~]# my_variable="Null"
[root@centos1 ~]# echo $my_variable
Null
[root@centos1 ~]# exit
exit
[root@centos1 ~]# echo $my_variable
I an Global now

子shell 甚至无法使用export 命令改变父shell中全局变量的值。

[root@centos1 ~]# my_variable="I an Global now"
[root@centos1 ~]# export my_variable
[root@centos1 ~]# echo $my_variable
I an Global now
[root@centos1 ~]# bash
[root@centos1 ~]# echo $my_variable
I an Global now
[root@centos1 ~]# my_variable="Null"
[root@centos1 ~]# export my_variable
[root@centos1 ~]# echo $my_variable
Null
[root@centos1 ~]# exit
exit
[root@centos1 ~]# echo $my_variable
I an Global now

尽管子shell重新定义并导出了变量my_variable,但父shell中的my_variable变量依然保留着原先的值。

6.3 删除环境变量

删除已存在的环境变量,可以用 unset 命令完成这个操作。在unset命令中引用环境变量时,记住不要使用$

[root@centos1 ~]# echo $my_variable
I an Global now
[root@centos1 ~]# unset my_variable
[root@centos1 ~]# echo $my_variable


窍门:在涉及环境变量名时,什么时候该使用$,什么时候不该使用$,如果用到变量,使用$ ; 如果要操作变量,不使用$ 。 这条规则有一个意外就是使用 printenv 显示某个变量的值时。

如果你再子进程中删除了一个全局环境变量,这只对子进程有效。该全局变量在父进程中依然可用。

和修改变量一样,在子shell中删除环境变量后,你无法将效果反映到父shell中。

6.4 默认的 shell 环境变量

默认情况下,bash shell 会用一些特定的环境变量来定义系统环境。这些变量在你的Linux系统上都是已经设置好了。只管放心使用。bash shell 源自当初的Uinx Bourne shell 因此保留了环境变量

出了默认的Bourne的环境变量,bash shell 还提供了一些自有的变量。

并不是所有的默认环境变量都会在运行set 命令时列出,尽管这些都是默认的环境变量,但并不是每一个都必须有一个值。

6.5 设置Path 环境变量

当你再shell命令行中输入一个外部命令时,shell 必须搜索系统来找对应的程序。PATH 环境变量定义了用于进行命令和程序查找的目录。本书使用的是Ubuntu 系统,PATH环境变量内容是这样的:

[root@centos1 ~]# echo $PATH
/usr/local/jdk8/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
/usr/sbin:/usr/bin:/home/hadoop/sbin:/home/hadoop/bin:/root/bin

PATH 中的目录使用冒号分隔。

如果命令或者程序的位置没有包括在PATH 变量中,那么如果不使用绝对路径,shell是没法找到的。如果shell找不到指定命令或者程序,它会产生一个错误信息:

[root@centos1 ~]# abc
-bash: abc: command not found

可以把新的搜索目录添加到现有的PATH环境变量中,无需从头定义。PATH中各个目录之间使用冒号分隔的,你只需要引用原来的PATH 值,然后再给这个字符串添加新的目录进行了。可以考虑一面的例子。

[root@centos1 ~]# echo $PATH
usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/home/hadoop/sbin:/home/hadoop/bin:/root/bin
[root@centos1 ~]# PATH=$PATH:/usr/local/jdk8/bin/
[root@centos1 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/home/hadoop/sbin:/home/hadoop/bin:/root/bin:/usr/local/jdk8/bin
[root@centos1 ~]# java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)


如果希望子shell 也能找到你的程序位置,一定要记得把修改后的PATH 环境变量导出

程序员通常办法是将单点符也加入PATH环境变量。该单点符代表当前目录

[root@centos1 ~]# PATH=$PATH:.
[root@centos1 ~]# cd /usr/local/jdk8/bin/
[root@centos1 bin]# java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

对PATH 变量的修改只能持续到退出或者重启系统。这种效果并不能一直持续。下节将介绍如果持久保持环境变量的修改效果。

6.6 定位系统环境变量

当你登入linux 启动一个bash shell 时,默认情况下bash 会在几个文件中查找命令,这些文件叫作启动文件或者环境文件。bash 检查启动文件取决于你启动bash shell的方式,启动bash shell有三种方式:

登录时作为默认登录shell

作为非登录shell的交互式shell

作为运行脚本的非交互shell

6.1.1 登录shell

当你登录Linux系统时,bash shell 会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令:

/etc/profile

$HOME/.bash_profile

$HOME/.bashrc

$HOME/.bash_login

$HOME/.profile

/etc/profile 文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件。

另外4个启动文件是针对用户的,可根据个人需求定制

1./etc/profile文件

/etc/profile 文件是bash shell 默认的主启动文件,只要你登陆了Linux系统。bash就会执行/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 /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

用到了一个特性 for 语句。它用来迭代/etc/profile.d 目录下的所有文件。(该语句在13节中详述) 这为Linux系统提供了一个放置特定应用程序启动文件的地方,当用户登录时,shell会执行这些文件, /etc/profile.d 目录下包含以下文件:

[root@centos1 bin]# ls -l /etc/profile.d/
total 72
-rw-r--r--. 1 root root 1150 Apr 12  2016 colorls.csh
-rw-r--r--. 1 root root 1179 Apr 12  2016 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 Jan 21  2016 glib2.csh
-rw-r--r--. 1 root root  192 Jan 21  2016 glib2.sh
-rw-r--r--. 1 root root   58 May 11  2016 gnome-ssh-askpass.csh
-rw-r--r--. 1 root root   70 May 11  2016 gnome-ssh-askpass.sh
-rw-r--r--. 1 root root 1745 May 12  2016 lang.csh
-rw-r--r--. 1 root root 2706 May 12  2016 lang.sh
-rw-r--r--. 1 root root  123 Jun  4  2014 less.csh
-rw-r--r--. 1 root root  121 Jun  4  2014 less.sh
-rw-r--r--. 1 root root  976 Sep 24  2011 qt.csh
-rw-r--r--. 1 root root  912 Sep 24  2011 qt.sh
-rw-r--r--. 1 root root 2142 Jul 24  2015 udisks-bash-completion.sh
-rw-r--r--. 1 root root  105 Jul 24  2015 vim.csh
-rw-r--r--. 1 root root  269 Jul 24  2015 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 会话时生效。

shell会按照以下顺序,运行第一个被找到的文件,余下的则被忽略:

$HOME/.bash_profile

$HOME/.bash_login

$HOME/.profile

这个列表并没有$HOME/.bashrc文件。这是因为该文件通常通过其他文件运行的。

CentOS Linux 系统中的.bash_profile 文件内容如下:

[root@centos1 bin]# cat ~/.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系统上,这个文件看起来如下:

[work@16-11-120 ~]$ cat .bashrc
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions

.bashrc文件有两个作用:一是查看/etc目录下通过的bashrc文件,而是为用户提供一个定制自己的命令别名(参看第五章)和 私有脚本函数 (将在第17章讲到) 的地方。

6.6.3 非交互式shell

最后一种shell是非交互式shell。系统执行shell脚本时 用的就是这种shell。不同的地方在于它没有命令提示符。当你在系统上运行脚本时,希望运行一些特定启动的命令。

窍门:脚本能以不同的方式执行。只是其中的某一些方式能够启动子shell。你会在第11章中学习到shell不同的执行方式。

为了处理这种情况,bash shell 提供了BASH_ENV 环境变量。当shell启动一个非交互式shell进程时,它会检查这个环境变量来查看要执行的启动文件。如果有指定的文件,shell会执行该文件里的命令,这通常包括shell脚本变量设置。

在默认的CentOS中 这个环境变量在默认情况下并未设置。如果变量未设置,printenv 命令会返回CLI提示符:

[work@16-11-120 ~]$ printenv BASH_ENV
[work@16-11-120 ~]$
[work@16-11-120 ~]$ echo $BASH_ENV
[work@16-11-120 ~]$

那如果BASH_ENV 变量没有设置,shell到哪里获取它们的环境变量呢? 有些shell脚本时通过启动一个子shell来执行的(参见第五章) 。 子shell可以继承父shell导出过的变量。

举例来说,如果父shell是登录shell, 在/etc/profile、/etc/profile.d/*.sh 和 $HOME/.bashrc文件中设置并导出了变量,用户执行脚本的子shell就能够继承这些变量。

但记住:由父shell设置但并未导出的变量都是局部变量。子shell无法继承局部变量。

对于那些不启动子shell的脚本,变量已经存在于当前shell中了。所以就算没有设置BASH_ENV,也可以使用当前shell的局部变量和全局变量。

6.6.4 环境变量持久化

对于全局环境变量来说(Linux系统所有的用户都需要使用的变量),可能更秦翔宇将新的或修改过的变量设置放在/etc/profile文件中。但这可不是什么好主意,如果升级了使用的发行版,这个文件会跟着更新,之前定制过的变量设置就没有了。

最好是在/etc/profile.d目录中创建一个以 .sh 结尾的文件。把所有新的或修改过的全局环境变量设置放在这个文件中。

大多数发行版中,存储个人用户永久性bash shell变量的地方是 $HOME/.bashrc 文件。这一点适用于所有类型的shell进程。但如果设置了BASH_ENV 变量,那么记住,除非它指向的是$HOME/.bashrc,否则你应该将非交互式shell的用户变量放在别的地方。

第五章中讲过的alias 命令设置就是不能持久的。你可以把自己的alias设置在$HOME/.bashrc启动文件中,使其效果持久化。

6.7 数组变量

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bash Linux
相关文章推荐