Shell环境变量

bash shell中使用环境变量在内存中存储有关shell会话和工作环境的数据。以便程序或shell中运行的脚本能够访问到它们。bash shell中的环境变量主要有两种

  • 全局变量
  • 局部变量

全局变量与局部变量

查看系统中所有全局变量,可以使用envprintenv命令。
要显示个别环境变量的值,可以使用printenv命令,但是不能用env命令。

#查看shell个别全局变量
printenv HOME
#查看shell个别全局变量:通过echo 以变量的形式输出
echo $HOME

系统的环境都是大写,定义属于用户自己局部变量时统一使用小写,避免冲突。

  • 定义局部变量
variable=Hello
echo $variable #输出:Hello

重要:变量名、等号和值之间没有空格。
如果在赋值表达式中加上了空格, bash shell就会把值当成一个单独的命令,变量值有空格需要使用引号。

variable="Hello word"
echo $variable #输出:Hello word
  • 定义全局变量

在设定全局环境变量的进程所创建的子进程中,该变量都是可见的。
创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局环境中。
父shell定义的全局变量,子shell的修改不会影响父shell的值。

variable="global variable ~~~~"
export variable
#开启子shell
bash
#子shell 输出一下
echo $variable #global variable ~~~~
#子shell修改
variable="子shell修改"
#子shell 输出一下
echo $variable #输出:子shell修改
#导出
export variable
#退出子shell
exit
#父shell输出
echo $variable #子shell的修改不会影响父shell:global variable ~~~~
  • 删除全局的变量
    在子shell中是无法删除父shell创建的全局变量。
#删除
unset variable
  • 默认的全局环境变量
    默认情况下,bash shell会用一些特定的环境变量来定义系统环境。这些变量在你的Linux系统上都已经设置好了,只管放心使用。bash shell源自当初的Unix Bourne shell,因此也保留了Unix Bourne shell里定义的那些环境变量。
    列举几个比较常见的环境变量:
变量 描述
CDPATH 冒号分隔的目录列表,作为cd命令的搜索路径
HOME 当前用户的主目录
PATH shell查找命令的目录列表,由冒号分隔
BASH 当前shell实例的全路径名
PWD 当前工作目录

设置PATH环境变量

当我们在shell命令行界面中输入一个外部命令时,shell必须搜索系统来找到对应的程序。PATH环境变量定义了用于进行命令和程序查找的目录:

#输出下
echo $PATH
#结果
/Users/*/.rvm/gems/ruby-2.3.0/bin:
/Users/*/.rvm/gems/ruby-2.3.0@global/bin:
/Users/*/.rvm/rubies/ruby-2.3.0/bin:
/Users/*/Desktop/development/flutter/bin:
/usr/local/bin:
/usr/bin:
/bin:
/usr/sbin:
/sbin:
/Users/*/.rvm/bin

输出的结果中显示了有10个可供shell用来查找命令和程序的路径。PATH中的目录使用冒号分隔。这些路径下分别都存放了不同的命令和程序,举个/bin的示例:

/bin目录下的程序与命令

如果命令或者程序的路径没有包括在PATH变量中,则不使用绝对路径的情况下,shell是没法找到的该程序的。

问题:应用程序放置可执行文件的目录常常不在PATH环境变量所包含的目录中。

解决:是保证PATH环境变量包含了所有存放应用程序的目录。可以把新的搜索目录添加到现有的PATH环境变量中,无需从头定义。PATH中各个目录之间是用冒号分隔的,我们只需要引用原来的PATH值,然后再给这个字符串添加新目录就行了。

举例终端启用flutter命令:

#查看path
echo $PATH
#结果不包含:/Users/*/Desktop/development/flutter/bin:
#通过终端启用
PATH=$PATH:~/Desktop/development/flutter/bin
#再次查看
echo $PATH
#结果包含:/Users/*/Desktop/development/flutter/bin:
#执行flutter
flutter -v
#不再提示找不到命令。

值得注意的是:对PATH变量的修改只能持续到退出或重启终端系统。这种效果并不能一直持续。

问题:如何让这种效果持续?

定位环境变量

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

  • 登录时作为默认登录shell
  • 作为非登录shell的交互式shell
  • 作为运行脚本的非交互shell
默认登录shell

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

  1. /etc/profile
#/etc/profile启动文件内容

# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi
  1. $HOME/.bash_profile
  2. $HOME/.bashrc
#macOS系统中.bashrc启动文件内容,非登录的交互式shell会以此未启动文件的。
export PATH="$PATH:$HOME/.rvm/bin"
  1. $HOME/.bash_login
  2. $HOME/.profile

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

另外4个启动文件是针对用户的,提供一个用户专属的启动文件来定义该用户所用到的环境变量,可根据个人需求定制,且都是隐藏文件。它们位于用户的HOME目录下,所以每个用户都可以编辑这些文件并添加自己的环境变 量,这些环境变量会在每次启动bash shell会话时生效。其中2和5我们在macOS中是比较熟悉的。

shell会按照下列顺序,运行第一个被找到的文件,余下的则被忽略(不会重复):
$HOME/.bash_profile=>$HOME/.bash_login=>$HOME/.profile 注:$HOME和波浪号~作用一样,都代表用户目录。

正是因为此规则的存在,我们有些时候只需要在$HOME/.profile中配置我们的PATH即可。

非登录的交互式shell

不是登录时启动的shell称为交互式shell。比如:在命令行提示符下敲入bash时启动。
bash是作为交互式shell启动,则不会访问/etc/profile文件,只会检查用户目录$HOME下的.bashrc文件。

运行脚本的非交互shell

非交互shell:系统执行shell脚本时会使用。不同的地方在于它没有命令行提示符。但是当我们在系统上运行脚本时,可能希望运行一些特定启动的命令。

为了处理这种情况,bash shell提供了BASH_ENV环境变量。当shell启动一个非交互式shell进程时,它会检查这个环境变量来查看要执行的启动文件。

macOS系统下运行echo $BASH_ENV查看,这个环境变量并未被设置。如果BASH_ENV变量没有设置,shell脚本如何获得它们的环境变量呢?

  • shell可以继承父shell到处的环境变量;
    但需要注意的是父shell中设置但却未export的变量,属于局部变量,子shell是无法获取的。也就是说执行脚本时,采用bash命令开启子shell便可以解决这个问题。
  • 不启动子shell的脚本,变量已经存在于当前shell中。
环境变量持久化

Linux在大多数发行版中,存储个人用户永久性bash shell变量的地方是$HOME/.bashrc文件。这一 点适用于所有类型的shell进程。

但如果设置了BASH_ENV变量,那么除非BASH_ENV指向的是 $HOME/.bashrc,否则应该将非交互式shell的用户变量放在别的地方。

macOS系统中,存储个人用户永久性bash shell变量的地方,便是对应的环境文件:~/.profile~/.bash_profile~/.bashrc(交互式shell生效)。其中~/.profile~/.bash_profile任意一个都可以定义我们的永久性bash shell变量。

#在`~/.profile`文件中定义
export LOVE="全局可用的环境变量love"
PEACE="局部环境变量,子shell不可用"
#在`~/.bash_profile`文件中定义
export SHARE="全局可用的环境变量share"
QI="局部环境变量,子shell不可用"
#终端shell,查看全局变量
env
#输出
LOVE=全局可用的环境变量love
SHARE=全局可用的环境变量share
#终端shell,查看永久局部变量
echo $PEACE
echo $QI
#输出
局部环境变量,子shell不可用
#开启非登陆交互式shell
bash
#子shell,查看可用全局变量
env
#输出
LOVE=全局可用的环境变量love
SHARE=全局可用的环境变量share
#子shell,查看可用的父Shell的局部变量
echo $PEACE
echo $QI
#输出为空

关于~/.bashrc,作为非登录式交互shell的启动文件,仅对非登录式交互shell生效:

#在`~/.bashrc`配置
export CHILD="子shell可用"
#重新打开终端,输入
env
#or
echo $CHILD
#都输出:空
 
#开启子shell(交互式shell)
bash
#查看子shell的全局变量
env 
#or
echo $CHILD
#输出
CHILD=子shell可用
子shell可用
#再次开启子shell
bash
#输入
env 
#or
echo $CHILD
#输出
CHILD=子shell可用
子shell可用
#如果`~/.bashrc`文件中配置
CHILD="子shell可用"
#输入
env
#输出:空
#输入
echo $CHILD
#输出
子shell可用
#再次开启子shell,`CHILD`变量将不再生效

数组变量

数组变量:环境变量设置多个值,放置在括号中,且值与值之间用空格隔开。

#定义数组变量
ARRAY_VAR=(one two three)
#输出数组变量
echo $ARRAY_VAR
#不会全部输出,只会输出数组变量中第一个元素
one

要引用数组变量中的某个元素,就必须用代表它在数组中位置的数值索引值。索引值要用方括号括起来:

echo ${ARRAY_VAR[2]}
#输出
three
#显示整个数组
echo ${ARRAY_VAR[*]}

可以用unset命令删除数组中的某个值,但是要小心。比如:

#删除索引为1的元素
unset ARRAY_VAR[1]
#输出整个数组
echo ${ARRAY_VAR[*]}
#删除成功
one three
#输出数组中索引为1的元素
echo ${ARRAY_VAR[1]}
#结果为:空

#输出索引为2的元素
echo ${ARRAY_VAR[2]}
#结果
3

参考资料:
Linux命令行与shell脚本编程大全

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345