什么是环境变量?
bash shell用一个叫作环境变量( environment variable)的特性来存储有关shell会话和工作环境的信息(这也是它们被称作环境变量的原因)。这项特性允许你在内存中存储数据,以便程序或shell中运行的脚本能够轻松访问到它们。这也是存储持久数据的一种简便方法。
在bash shell中,环境变量分为两类:
- 全局变量
- 局部变量
全局环境变量
全局环境变量对于shell会话和所有生成的子shell都是可见的。局部变量则只对创建它们的shell可见。这让全局环境变量对那些所创建的子shell需要获取父shell信息的程序来说非常有用。
Linux系统在你开始bash会话时就设置了一些全局环境变量。系统环境变量基本上都是使用全大写字母,以区别于普通用户的环境变量。
查看全局变量
$ printenv HOSTNAME=server01.class.edu SELINUX_ROLE_REQUESTED= TERM=xterm SHELL=/bin/bash HISTSIZE=1000 [...] HOME=/home/Christine LOGNAME=Christine [...] G_BROKEN_FILENAMES=1 _=/usr/bin/printenv
查看某个全局变量
只能使用printenv命令,不要用env命令。
$ printenv HOME /home/Christine $ $ env HOME env: HOME: No such file or directory $
也可以使用echo显示变量的值。在这种情况下引用某个环境变量的时候,必须在变量前面加上一个美元符( $)。
$ echo $HOME /home/Christine $
局部环境变量
顾名思义,局部环境变量只能在定义它们的进程中可见。尽管它们是局部的,但是和全局环境变量一样重要。事实上, Linux系统也默认定义了标准的局部环境变量。不过你也可以定义自己的局部变量,如你所想,这些变量被称为用户定义局部变量。
查看局部环境变量的列表有点复杂。遗憾的是,在Linux系统并没有一个只显示局部环境变量的命令。 set命令会显示为某个特定进程设置的所有环境变量,包括局部变量、全局变量
以及用户定义变量。
$ set BASH=/bin/bash [...] BASH_ALIASES=() BASH_ARGC=() BASH_ARGV=() BASH_CMDS=() BASH_LINENO=() BASH_SOURCE=() [...] colors=/etc/DIR_COLORS my_variable='Hello World' [...] $
设置用户定义变量
可以在bash shell中直接设置自己的变量。本节将介绍怎样在交互式shell或shell脚本程序中创建自己的变量并引用它们。
设置局部用户定义变量
一旦启动了bash shell(或者执行一个shell脚本),就能创建在这个shell进程内可见的局部变量了。可以通过等号给环境变量赋值,值可以是数值或字符串。
[root@instance-3lm099to ~]# my_wife=hanmeimei [root@instance-3lm099to ~]# echo $my_wife hanmeimei [root@instance-3lm099to ~]# my_money=1000000 [root@instance-3lm099to ~]# echo $my_money 1000000
如果要给变量赋一个含有空格的字符串值,必须用单引号来界定字符串的首和尾。
[root@instance-3lm099to ~]# my_motto='Books are the ladder of human progress' [root@instance-3lm099to ~]# echo $my_motto Books are the ladder of human progress
如果是你自己创建的局部变量或是shell脚本,请使用小写字母。系统环境变量都是大写字母。 变量名区分大小写。 请大家最好遵守这个规则,否则会带来灾难。
记住,变量名、等号和值之间没有空格,这一点非常重要。如果在赋值表达式中加上了空格,bash shell就会把值当成一个单独的命令:
[root@instance-3lm099to ~]# my_wife = hanmeimei
-bash: my_wife: command not found
设置了局部环境变量后,就能在shell进程的任何地方使用它了。但是,如果生成了另外一个shell,它在子shell中就不可用。
[root@instance-3lm099to ~]# my_job=teacher [root@instance-3lm099to ~]# bash [root@instance-3lm099to ~]# echo $my_job [root@instance-3lm099to ~]# exit exit [root@instance-3lm099to ~]# echo $my_job teacher
类似地,如果你在子进程中设置了一个局部变量,那么一旦你退出了子进程,那个局部环境变量就不可用。
[root@instance-3lm099to ~]# bash [root@instance-3lm099to ~]# my_name='zhao kuang ying' [root@instance-3lm099to ~]# echo $my_name zhao kuang ying [root@instance-3lm099to ~]# exit exit [root@instance-3lm099to ~]# echo $my_name
可以通过将局部的用户定义变量变成全局变量来改变这种情况。
设置全局环境变量
在设定全局环境变量的进程所创建的子进程中,该变量都是可见的。创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局环境中。
这个过程通过export命令来完成,变量名前面不需要加$。
[root@instance-3lm099to ~]# my_age=33 [root@instance-3lm099to ~]# export my_age [root@instance-3lm099to ~]# echo $my_age 33 [root@instance-3lm099to ~]# bash [root@instance-3lm099to ~]# echo $my_age 33 [root@instance-3lm099to ~]# exit exit [root@instance-3lm099to ~]# echo $my_age 33
修改子shell中全局环境变量并不会影响到父shell中该变量的值。
[root@instance-3lm099to ~]# my_book='this is right' [root@instance-3lm099to ~]# export my_book [root@instance-3lm099to ~]# echo $my_book this is right [root@instance-3lm099to ~]# bash [root@instance-3lm099to ~]# echo $my_book this is right [root@instance-3lm099to ~]# my_book='《this is right》' [root@instance-3lm099to ~]# echo $my_book 《this is right》 [root@instance-3lm099to ~]# exit exit [root@instance-3lm099to ~]# echo $my_book this is right
即使使用export命令,子shell也无法改变父shell中全局环境变量的值。
删除环境变量
删除用unset命令,记住不要使用$。
[root@instance-3lm099to ~]# my_name=YangJie [root@instance-3lm099to ~]# echo $my_name YangJie [root@instance-3lm099to ~]# unset my_name [root@instance-3lm099to ~]# echo $my_name
和修改变量一样,在子shell中删除全局变量后,你无法将效果反映到父shell中。
ps:在涉及环境变量名时,什么时候该使用,什么时候不该使用,实在让人摸不着头脑。记住一点就行了:如果要用到变量,使用;如果要操作变量,不使用。这条规则的一个例外就是使用显示某个变量的值。
默认的环境变量
默认情况下, bash shell会用一些特定的环境变量来定义系统环境。这些变量在你的Linux系统上都已经设置好了,只管放心使用。 bash shell源自当初的Unix Bourne shell,因此也保留了Unix Bourne shell里定义的那些环境变量。
bash shell提供的与Unix Bourne shell兼容的环境变量
CDPATH 冒号分隔的目录列表,作为cd命令的搜索路径
HOME 当前用户的主目录
IFS shell 用来将文本字符串分割成字段的一系列字符
MAIL 当前用户收件箱的文件名( bash shell会检查这个文件,看看有没有新邮件)
MAILPATH 冒号分隔的当前用户收件箱的文件名列表( bash shell会检查列表中的每个文件,看看有没有新邮件)
OPTARG getopts 命令处理的最后一个选项参数值
OPTIND getopts 命令处理的最后一个选项参数的索引号
PATH shell 查找命令的目录列表,由冒号分隔
PS1 shell 命令行界面的主提示符
PS2 shell 命令行界面的次提示符
bash shell一些自有的变量
BASH 当前shell实例的全路径名
BASH_ALIASES 含有当前已设置别名的关联数组
BASH_ARGC 含有传入子函数或shell脚本的参数总数的数组变量
BASH_ARCV 含有传入子函数或shell脚本的参数的数组变量
BASH_CMDS 关联数组,包含shell执行过的命令的所在位置
BASH_COMMAND shell正在执行的命令或马上就执行的命令
BASH_ENV 设置了的话,每个bash脚本会在运行前先尝试运行该变量定义的启动文件
BASH_EXECUTION_STRING 使用bash -c选项传递过来的命令
BASH_LINENO 含有当前执行的shell函数的源代码行号的数组变量
BASH_REMATCH 只读数组,在使用正则表达式的比较运算符=~进行肯定匹配( positive match)时,包含了匹配到的模式和子模式
BASH_SOURCE 含有当前正在执行的shell函数所在源文件名的数组变量
BASH_SUBSHELL 当前子shell环境的嵌套级别(初始值是0)
BASH_VERSINFO 含有当前运行的bash shell的主版本号和次版本号的数组变量
BASH_VERSION 当前运行的bash shell的版本号
BASH_XTRACEFD 若设置成了有效的文件描述符( 0、 1、 2),则'set -x'调试选项生成的跟踪输出可被重定向。通常用来将跟踪输出到一个文件中
BASHOPTS 当前启用的bash shell选项的列表
BASHPID 当前bash进程的PID
COLUMNS 当前bash shell实例所用终端的宽度
COMP_CWORD COMP_WORDS 变量的索引值,后者含有当前光标的位置
COMP_LINE 当前命令行
COMP_POINT 当前光标位置相对于当前命令起始的索引
COMP_KEY 用来调用shell函数补全功能的最后一个键
COMP_TYPE 一个整数值,表示所尝试的补全类型,用以完成shell函数补全
COMP_WORDBREAKS Readline 库中用于单词补全的词分隔字符
COMP_WORDS 含有当前命令行所有单词的数组变量
COMPREPLY
COPROC 含有由shell函数生成的可能填充代码的数组变量占用未命名的协进程的I/O文件描述符的数组变量
DIRSTACK 含有目录栈当前内容的数组变量
EMACS 设置为't'时,表明emacs shell缓冲区正在工作,而行编辑功能被禁止
ENV 如果设置了该环境变量,在bash shell脚本运行之前会先执行已定义的启动文件(仅用于当bash shell以POSIX模式被调用时)
EUID 当前用户的有效用户ID(数字形式)
FCEDIT 供fc命令使用的默认编辑器
FIGNORE 在进行文件名补全时可以忽略后缀名列表,由冒号分隔
FUNCNAME 当前执行的shell函数的名称
FUNCNEST 当设置成非零值时,表示所允许的最大函数嵌套级数(一旦超出,当前命令即被终止)
GLOBIGNORE 冒号分隔的模式列表,定义了在进行文件名扩展时可以忽略的一组文件名
GROUPS 含有当前用户属组列表的数组变量
histchars 控制历史记录扩展,最多可有3个字符
HISTCMD 当前命令在历史记录中的编号
HISTCONTROL 控制哪些命令留在历史记录列表中
HISTFILE 保存shell历史记录列表的文件名(默认是.bash_history)
HISTFILESIZE
HISTTIMEFORMAT 最多在历史文件中存多少行,如果设置了且非空,就用作格式化字符串,以显示bash历史中每条命令的时间戳
HISTIGNORE 由冒号分隔的模式列表,用来决定历史文件中哪些命令会被忽略
HISTSIZE 最多在历史文件中存多少条命令
HOSTFILE shell在补全主机名时读取的文件名称
HOSTNAME 当前主机的名称
HOSTTYPE 当前运行bash shell的机器
IGNOREEOF shell 在退出前必须收到连续的EOF字符的数量(如果这个值不存在,默认是1)
INPUTRC Readline 初始化文件名(默认是.inputrc)
LANG shell的语言环境类别
LC_ALL 定义了一个语言环境类别,能够覆盖LANG变量
LC_COLLATE 设置对字符串排序时用的排序规则
LC_CTYPE 决定如何解释出现在文件名扩展和模式匹配中的字符
LC_MESSAGES 在解释前面带有$的双引号字符串时,该环境变量决定了所采用的语言环境设置
LC_NUMERIC 决定着格式化数字时采用的语言环境设置
LINENO 当前执行的脚本的行号
LINES 定义了终端上可见的行数
MACHTYPE 用“ CPU公司系统”( CPU-company-system)格式定义的系统类型
MAPFILE 一个数组变量,当mapfile命令未指定数组变量作为参数时,它存储了mapfile所读
入的文本
MAILCHECK shell查看新邮件的频率(以秒为单位,默认值是60)
OLDPWD shell之前的工作目录
OPTERR 设置为1时, bash shell会显示getopts命令产生的错误
OSTYPE 定义了shell所在的操作系统
PIPESTATUS 含有前台进程的退出状态列表的数组变量
POSIXLY_CORRECT 设置了的话, bash会以POSIX模式启动
PPID bash shell父进程的PID
PROMPT_COMMAND 设置了的话,在命令行主提示符显示之前会执行这条命令
PROMPT_DIRTRIM 用来定义当启用了w或W提示符字符串转义时显示的尾部目录名的数量。被删除的
目录名会用一组英文句点替换
PS3 select命令的提示符
PS4 如果使用了bash的-x选项,在命令行之前显示的提示信息
PWD 当前工作目录
RANDOM 返回一个0~ 32767的随机数(对其的赋值可作为随机数生成器的种子)
READLINE_LINE 当使用bind –x命令时,存储Readline缓冲区的内容
READLINE_POINT 当使用bind –x命令时,表示Readline缓冲区内容插入点的当前位置
REPLY read命令的默认变量
SECONDS 自从shell启动到现在的秒数(对其赋值将会重置计数器)
SHELL bash shell的全路径名
SHELLOPTS 已启用bash shell选项列表,列表项之间以冒号分隔
SHLVL shell的层级;每次启动一个新bash shell,该值增加1
TIMEFORMAT 指定了shell的时间显示格式
TMOUT select和read命令在没输入的情况下等待多久(以秒为单位)。默认值为0,表示
无限长
TMPDIR 目录名,保存bash shell创建的临时文件
UID 当前用户的真实用户ID(数字形式)
设置 PATH 环境变量
当你在shell命令行界面中输入一个外部命令时, shell必须搜索系统来找到对应的程序。 PATH环境变量定义了用于进行命令和程序查找的目录。
[root@instance-3lm099to ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/java/jdk1.8.0_171//bin:/usr/local/java/jdk1.8.0_171//jre/bin:/usr/local/hbase/hbase-0.98.11-hadoop2/bin:/usr/local/hadoop/hadoop-1.1.2/bin:/root/bin
PATH中的目录使用冒号分隔。
如果命令或者程序的位置没有包括在PATH变量中,那么如果不使用绝对路径的话, shell是没法找到的。如果shell找不到指定的命令或程序,它会产生一个错误信息:
[root@instance-3lm099to ~]# redis
-bash: redis: command not found
问题是,应用程序放置可执行文件的目录常常不在PATH环境变量所包含的目录中。解决的办法是保证PATH环境变量包含了所有存放应用程序的目录。
可以把新的搜索目录添加到现有的PATH环境变量中,无需从头定义。 PATH中各个目录之间是用冒号分隔的。你只需引用原来的PATH值,然后再给这个字符串添加新目录就行了。可以参考下面的例子。
$ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin: /sbin:/bin:/usr/games:/usr/local/games $ $ PATH=$PATH:/home/christine/Scripts $ $ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/ games:/usr/local/games:/home/christine/Scripts $ $ myprog The factorial of 5 is 120. $
将目录加到PATH环境变量之后,你现在就可以在虚拟目录结构中的任何位置执行程序。
$ cd /etc $ $ myprog The factorial of 5 is 120 $
ps:如果希望子shell也能找到你的程序的位置,一定要记得把修改后的PATH环境变量导出。
通常将单点符也加入PATH环境变量。该单点符代表当前目录 。
$ PATH=$PATH:. $ $ cd /home/christine/Old_Scripts $ $ myprog2 The factorial of 6 is 720 $
对PATH变量的修改只能持续到退出或重启系统。
定位系统环境变量
登录 shell
当你登录Linux系统时, bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令:
- /etc/profile
- $HOME/.bash_profile
- $HOME/.bashrc
- $HOME/.bash_login
- $HOME/.profile
/etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件。
交互式 shell 进程
如果你的bash shell不是登录系统时启动的(比如是在命令行提示符下敲入bash时启动),那么你启动的shell叫作交互式shell。交互式shell不会像登录shell一样运行,但它依然提供了命令行提示符来输入命令。
如果bash是作为交互式shell启动的,它就不会访问/etc/profile文件,只会检查用户HOME目录中的.bashrc文件。
非交互式 shell
最后一种shell是非交互式shell。系统执行shell脚本时用的就是这种shell。不同的地方在于它没有命令行提示符。但是当你在系统上运行脚本时,也许希望能够运行一些特定启动的命令。