linux shell环境变量


环境变量的功能

回到顶部

环境变量也是bash的变量,但是和bash的普通变量又有所区别,环境变量可以被bash的子进程继承,而普通变量不可以被继承。 环境变量可以帮我们达到很多功能,包括家目录的切换、命令行提示符的定制显示、命令可执行文件搜索的路径等。
可以通过env和export命令查看当前bash下的所有环境变量。
用env观察环境变量与常见环境变量说明
列出当前shell下所有的环境变量:

              [peter@study ~]$ env
              HOSTNAME=study.centos.peter #主机名
              TERM=xterm #终端机类型
              SHELL=/bin/bash #当前Shell
              HISTSIZE=1000 #历史命令的记录条数,CentOS默认1000条
              OLDPWD=/home/peter #上一个工作目录
              LC_ALL=en_US.utf8 #语系相关
              USER=peter #用户名
              LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:
              or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:
              *.tar=01... #颜色显示
              MAIL=/var/spool/mail/peter #用户mailbox位置
              PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin#命令执行搜索路径
              PWD=/home/peter #当前用户工作目录(利用pwd取出!)
              LANG=zh_TW.UTF-8 #语系相关
              HOME=/home/peter #用户家目录
              LOGNAME=peter # 登入者用来登入的账号名称
              _=/usr/bin/env # 上一次使用的命令的最后一个参数(或命令本身)
            
env是environment(环境)的简写,列出所有的环境变量。使用export也会是一样的内容,export还有其他额外的功能,我们稍后介绍。 下面是一些常用环境变量的介绍:
HOME:代表用户的家目录。我们使用cd ~或者直接cd切换到用户家目录。读取的就是这个变量, 有很多程序都可能会读取到这个变量;
SHELL:当前环境运行的SHELL程序,Linux默认使用/bin/bash;
HISTSIZE:历史命令相关,bansh可以记录的命令的最大条数;
MAIL:使用mail命令收信时,系统会去读取的邮件信箱文件(mailbox);
PATH:执行文件的搜寻路径,目录与目录中间以冒号(:)分隔, 文件的搜寻是依序由PATH的变量内的目录来查询;
LANG:语系相关。一般来说,中文编码通常是zh.Big5或者是zh.UTF-8;
RANDOM:随机数变量!目前大多数的distributions都会有随机数生成器,那就是/dev/random这个文件。 我们可以透过这个随机数文件相关的变量($RANDOM)来随机取得随机数。 在BASH的环境下,RANDOM变量介于0~32767之间,echo $RANDOM时,系统就会主动的随机取出一个介于0~32767的数值。 会随机取出0~9之间的数值,利用declare声明数值类型:
              [peter@study ~]$ declare -i number=$RANDOM*10/32768 ; echo $number
              8
            

用set观察所有变量 (含环境变量与自定义变量)
bash可不只有环境变量,还有一些与bash操作接口相关的变量,以及用户自己定义的变量。 使用set命令可以观察这些变量,除了环境变量之外,set还会将bash内的所有变量都显示出来!
env和export只能列出当前bash的环境变量,但是如果想要查看当前bash的所有变量,就得用到set命令,set命令除了列出当前bash的环境变量,也会列出bash中的自定义变量。

              [peter@study ~]$ set
              BASH=/bin/bash #bash的主程序路径
              BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
              BASH_VERSION='4.2.46(1)-release' #这两行是bash的版本!
              COLUMNS=90 #当前终端机环境下,字段的字符长度
              HISTFILE=/home/peter/.bash_history #保存历史命令的文件
              HISTFILESIZE=1000 #(与上个变量有关)历史记录文件保存的命令最大条数。
              HISTSIZE=1000 #目前环境下,内存中记录的历史命令最大条数。
              IFS=$' \t\n' #默认的分隔符
              LINES=20 #当前终端机的最大行数
              MACHTYPE=x86_64-redhat-linux-gnu #机器类型
              OSTYPE=linux-gnu #操作系统的类型!
              PS1='[\u@\h \W]\$ ' 命令提示字符,决定了命令提示符的显示。也就是我们常见的[root@www ~]#或[peter ~]$的设定值啦!
              PS2='> ' #使用换行符(\)之后的提示字符
              $ #当前shell进程的PID
              ? #上一条命令执行的返回值。
              ...
            
一般环境变量都会用大写字母,以表示和普通变量的区别。 不过并不是所有大写形式的变量都是环境变量,有一些bash自己定义的变量也会用大写字母表示,这些变量和bash的设置有关,例如PS变量。
上面比较重要的变量有下面这几个:
$:当前shell进程的PID(Process ID).想要知道当前shell进程的PID,可以用:echo $$!出现的数字就是PID号。
?:上一条命令执行的返回值。问号也是一个特殊的变量,命令在执行完后都会返回一个执行后的代码。 一般来说,命令成功执行会返回0,如果执行过程发生错误,就会返回非0的错误代码。
我们以底下的例子来看看:
              [peter@study ~]$ echo $SHELL
              /bin/bash #可顺利显示!没有错误!
              [peter@study ~]$ echo $?
              0 #没问题,返回值为0
              [peter@study ~]$ 12name=peter
              bash: 12name=peter: command not found... #发生错误了!bash 回报有问题
              [peter@study ~]$ echo $?
              127 #有问题,返回错误代码(非0),错误代码返回值依据软件而有不同,可以利用这个返回值来查找错误的原因!
              [peter@study ~]$ echo $?
              0 #怎么又变成正确了?"?"只与上一个执行命令有关,上一个命令echo $?正确执行没有错误,所以是0!
            
OSTYPE, HOSTTYPE, MACHTYPE:主机硬件与内核的等级。目前个人计算机的CPU主要分为32/64位,其中32位又可分为i386,i586,i686,而64位则称为x86_64。 由于不同等级的CPU命令集不太相同,因此软件可能会针对某些CPU进行优化,以求取较佳的软件性能。
所以软件就有i386, i686及x86_64之分。目前主流硬件几乎都是x86_64! 因此CentOS 7开始,已经不支持 i386兼容模式的安装光盘了! 要留意的是,较高阶的硬件通常会向下兼容旧有的软件,但较高阶的软件可能无法在旧机器上面安装! 你可以在x86_64的硬件上安装i386的Linux操作系统,但是无法在i686的硬件上安装x86_64的Linux操作系统!这点得要牢记在心!

export: 自定义变量转成环境变量

回到顶部

用户登录Linux并取得bash之后,bash就是一个独立的进程,每个进程都有一个进程标识符PID标识该进程。 接下来在这个bash下运行的任何命令都是由这个bash进程衍生出来的,运行的命令就被称为子进程。 父进程与子进程相关性示意图如下:
父进程与子进程相关性示意图 如上所示,整个命令运作的环境是实线的部分,我们在原本的bash下执行另一个bash,结果操作的环境接口会跑到第二个bash去(就是子进程), 那原本的bash进入暂停sleep状态。 若要回到原本的bash, 就执行exit或logout退出第二个bash。
子进程仅会继承父进程的环境变量, 子进程不会继承父进程的自定义变量! 原本bash的自定义变量在进入子进程后就会消失不见,一直到你离开子进程并回到原本的父进程后,这个变量才会出现!
export命令可以将自定义变量变成环境变量,就可以让自定义变量继续在子进程中使用,:

              [peter@study ~]$ export 变量名称
            
这东西用在分享自己的变量设定给后来呼叫的文件或其他程序啦! 像常常在自己的主控文件后面呼叫其他附属文件(类似函式的功能),但是主控文件与附属文件内都有相同的变量名称, 若一再重复设定时,要修改也很麻烦,此时只要在原本的第一个文件内设定好 export 变量 , 后面所呼叫的文件就能够使用这个变量设定了! 而不需要重复设定,这非常实用于shell script当中喔!
export不接任何参数可以将所有的环境变量列出来:
              [peter@study ~]$ export
              declare -x HISTSIZE="1000"
              declare -x HOME="/home/peter"
              declare -x HOSTNAME="study.centos.peter"
              declare -x LANG="zh_TW.UTF-8"
              declare -x LC_ALL="en_US.utf8"
              ...
            
可以使用declare将环境变量转成自定义变量!

影响显示结果的语系变量 (locale)

回到顶部

当我们使用man command去查询某个数据的说明文件时,说明文件的内容可能会因为使用的语系不同而产生乱码。 另外,利用ls查询文件的时候,也可能会出现乱码的情况。
导致这些问题的原因其实就是语系的问题。
目前大多数的Linux distributions已经支持日渐流行的UTF-8了,也都支持大部分的国家语系。
locale命令可以查询当前Linux支持的语系:

              [peter@study ~]$ locale -a
              ....
              zh_CN
              zh_CN.big5 #big5中文编码
              zh_CN.euctw
              zh_CN.utf8 #utf8中文编码
              zu_ZA
              zu_ZA.iso88591
              zu_ZA.utf8
            
中文语系至少支持了两种以上的编码,一种是目前常见的big5,另一种则是越来越流行的utf-8编码。
那么我们如何修订这些编码呢?其实可以通过底下这些变量:
              [peter@study ~]$ locale #不加任何选项与参数
              LANG=en_US.UTF-8 #主语言的环境
              LANGUAGE=en_US
              LC_CTYPE="en_US.UTF-8" #字符(文字)辨识的编码
              LC_NUMERIC=zh_CN.UTF-8 #数字系统的显示讯息
              LC_TIME="en_US.UTF-8" #时间系统的显示数据
              LC_COLLATE="en_US.UTF-8" #字符串的比较与排序等
              LC_MONETARY=zh_CN.UTF-8 #币值格式的显示等
              LC_MESSAGES="en_US.UTF-8" #讯息显示的内容,如菜单、错误讯息等
              LC_PAPER=zh_CN.UTF-8
              LC_NAME=zh_CN.UTF-8
              LC_ADDRESS=zh_CN.UTF-8
              LC_TELEPHONE=zh_CN.UTF-8
              LC_MEASUREMENT=zh_CN.UTF-8
              LC_IDENTIFICATION=zh_CN.UTF-8
              LC_ALL= #整体语系的环境
            
可以逐一设定每个与语系相关的变量,但事实上,只需设定LANG或者是LC_ALL,其他的语系变量会被这两个变量所覆盖! 为什么在Linux主机的终端机接口(tty1 ~ tty6)的环境下, 如果设定LANG=zh_TW.utf8这个设定值生效后,使用man或者其他讯息输出时, 都会有一堆乱码,尤其是使用 ls -l 这个参数时?
因为在 Linux 主机的终端机接口环境下是无法显示像中文这么复杂的编码文字, 所以就会产生乱码了。
也就是如此,我们才会必须要在tty1 ~ tty6的环境下, 加装一些中文化接口的软件,才能够看到中文! 不过,如果你是在 MS Windows 主机以远程联机服务器的软件联机到主机的话,文字接口确实是可以看到中文的。
此时反而你得要在 LC_ALL 设定中文编码才好呢! 无论如何,如果发生一些乱码的问题,那么设定系统里面保有的语系编码, 例如:en_US 或 en_US.utf8 等等的设定,应该就 OK 的啦!
好了,那么系统默认支持多少种语系呢?
当我们使用locale时,系统是列出目前Linux主机内保有的语系文件, 这些语系文件都放置在: /usr/lib/locale/ 这个目录中。
你当然可以让每个使用者自己去调整自己喜好的语系,但是整体系统默认的语系定义在哪里呢?
其实就是在 /etc/locale.conf 啰!这个文件在 CentOS 7.x 的内容有点像这样:
              [peter@study ~]$ cat /etc/locale.conf
              LANG=zh_TW.utf8
              LC_NUMERIC=zh_TW.UTF-8
              LC_TIME=zh_TW.UTF-8
              LC_MONETARY=zh_TW.UTF-8
              LC_PAPER=zh_TW.UTF-8
              LC_MEASUREMENT=zh_TW.UTF-8
            
你也可以自行将他改成你想要的语系编码即可。
假设你有一个纯文本文件原本是在 Windows 底下建立的,那么这个文件默认可能是big5 的编码格式。
在你将这个文件上传到 Linux 主机后,在 X window 底下打开时,咦!怎么中文字通通变成乱码了?
Linux目前大多默认是UTF-8显示!你只要将开启该文件的软件编码由utf8改成big5就能够看到正确的中文了!
原本是中文语系,所有显示的数据通通是中文。但为了网页显示的关系,需要将输出转成英文(en_US.utf8)的语系来展示才行。
但又不想要写入配置文件!毕竟是暂时显示用的~那该如何处理?
其实不很难,重点是LANG及LC_ALL而已!但在CentOS 7当中,你要让LC_ALL生效时,得要使用export转成环境变量才行:
              [peter@study ~]$ locale
              LANG=zh_TW.UTF-8
              LC_CTYPE="zh_TW.UTF-8"
              LC_NUMERIC="zh_TW.UTF-8"
              LC_TIME="zh_TW.UTF-8"[peter@study ~]$ LANG=en_US.utf8; locale
              [peter@study ~]$ export LC_ALL=en_US.utf8; locale
            

上一篇文章《什么是linux命令》提到,linux在执行命令之前,其实是经由shell来处理的。Shell等待用户的终端输入,根据用户输入的命令名字, 在环境变量PATH指定的路径下寻找和命令名字匹配的可执行文件,然后创建子进程,该子进程会被linux内核调度执行,在子进程中加载可执行文件并执行。 这就是一条命令执行的一个完整过程。当然这里面涉及的细节,不是一两篇文章可以说的清楚的。后面我会在不同的文章中说明这些细节。shell作为用户和linux内核沟通的桥梁, 可以认为shell是linux内核与用户沟通的默认官方代理人,有很多种不同版本的shell,最常见的是bash shell。所以当我们提到shell的时候,默认就是指的bash shell。 如果你对这个代理人不满意,也可以更换代理人。你甚至可以把python解释器或者perl解释器作为linux的shell。不过几乎没有人会对bash shell不满意,也很少有人这么做。 Bash作为linux的官方默认代理人已是历史约定。这么说只是让大家理解shell,它并没有那么神秘,和其他的脚本解释器,甚至我们敲的命令都是一样的,你可以在shell里再执行shell, 像这样直接敲:bash,就会进入一个新的shell。至于怎么更换默认的shell,这不是这篇文章的内容。以后我会另外一篇文章说明怎么更换默认的shell。
我们回到本文的主题,上面提到shell是去环境变量PATH指定的路径中寻找命令,那么什么是环境变量呢?简单来讲,环境变量是用来设置linux应用程序运行环境的变量。 我们经常接触的环境变量有PATH、HOME、HISTSIZE、HOSTNAME、PS1、PS2等。在shell命令行下可通过“echo $环境变量名”打印环境变量的值, 也可通过env或者export命令查看系统中已有的环境变量。环境变量配置的是应用层的环境,由应用层程序设置并使用。不同的应用程序会关注不同的环境变量。 比如很多时候PATH只会由shell关注,我们经常会配置的java环境变量,会由java虚拟机关注,其他的应用程序是不会关注的。如果你在命令行下只敲了cd,不带路径名, 那么cd就会关注HOME这个环境变量,并切换到HOME环境变量指定的路径中,这个就是登录用户的家目录了。不过cd是shell的内置命令,所以HOME也主要是shell关注的。 我最开始理解环境变量的时候,总是认为它们是内核的一部分,其实环境变量和内核没有多大的关系。环境变量并不是用来配置linux内核的。如果你想对linux内核进行配置, 可以在内核编译配置阶段通过make menuconfig配置或者在内核运行过程中通过sysctl命令配置。
环境变量是用来配置应用程序运行环境的变量,所以环境变量是和应用程序息息相关的,而应用程序在运行阶段是以linux进程的形式存在的,每个进程都有自己的环境变量, 那么这些环境变量存放在哪里呢?他们是从哪里来的呢? 这里我们需要从进程的虚拟地址空间布局着手。每个linux进程(shell也是一个linux进程)都有自己独立的进程虚拟地址空间,进程虚拟地址空间分为内核空间和用户空间, 通常所有的linux进程虚拟地址空间的内核空间896M以内的空间会直接线性映射到物理内存的0-896M空间内。为什么是896M,这也是历史条件下形成的。 剩下的虚拟地址空间内核会按照自己的分页机制,间接的映射到物理内存中。关于这部分内容就是linux内核的内存管理部分了,本文不作深入讨论。 后面会专门抽出时间写一篇内存管理的文章。
说的环境变量,为什么会扯到进程虚拟地址空间呢?因为我们的环境变量是以环境变量表(数组)的形式存放在进程虚拟地址空间的用户空间里。这也是我上面说环境变量和内核没什么关系的原因。 进程虚拟地址空间的布局图如下图所示:
Linux内核在内存中的布局 进程虚拟空间布局 这张进程虚拟内存布局图是本人所画的《linux内核原理大图》的局部。该图目前还在创作中,已完成大部分。关注本头条号,可随时关注本人最新文章。 图中深红色的gap上方为内核空间,gap下方为用户空间。内核空间和用户空间有gap隔开。这也是内核的一种保护机制。这张内存布局图非常重要, 在linux下学习c/c++编程的人应该对这张图比较熟悉,因为不管是c/c++还是linux内核,原理性的东西都是围绕着这张图展开的。这张图毫不夸张的说, 就是打开linux环境下c/c++基础编程的金钥匙。
在用户空间的最上部,stack和gap中间,就是环境变量表所存放的位置了。我们的进程如果要想获取环境变量或者设置环境变量,都是从这个内存区域获取和设置的。 该部分会通过内核分页机制映射到物理内存中。
那么这里的环境变量又是从哪里来的呢?在《什么是linux命令》一文中,我们指出,linux运行一条命令,就会创建出一个进程,而这个进程是由shell创建的。 也就是你在linux下执行的任何命令所创建的进程,都是shell的子进程。所以一个进程的环境变量,大部分都是从shell进程继承而来的。 子又生孙,孙又生子,子子孙孙无穷尽也,而山不在高...
那么shell进程的环境变量又是从哪里来的呢? 我们前面说过,shell作为linux内核与用户打交道的代理人,所以要面对各型各色的人, 每个人都有不同的喜好。我们上文提到,可能会有人在小角落里密谋想要替换掉bash shell,Shell为了留住大家的心,适应不同人的喜好,就允许每个用户设置自己的环境变量。所以shell的环境变量其实就是用户自己设置的。 所以我们经常说环境变量的时候,前面总是加上shell,叫shell环境变量。每次shell启动的时候,都会读取一系列的环境配置文件。 将环境配置文件中环境变量的值读到自己内存空间的位置, 也就是上图所说的位置啦。然后由shell生成的子进程就会继承shell的环境变量。那么shell(我们这里主要说的bash)的环境变量配置文件存放在哪里呢? 有哪些设置环境变量的方法呢?我知道如果文章写的太长,就没有人会有耐心看下去的,所以我会在下一篇文章中说明shell环境变量的配置。因为平时还要养家糊口, 我的出文速度有点慢,所以机智的你,在还没等到我的文章之前,就已经找到答案了。那么这篇文章也算起到抛砖引玉的作用了。
我们在知道一样东西的来龙去脉后,是不是学习起来就更加顺手了呢。当然还是那句话,你知道的越多,不知道的也越多。这边文章又挖了很多坑, 欢迎关注本头条号,我们一起挖坑,一起填坑。本人水平有限,如果文章有误,欢迎批评指正。

initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读