Bash 中的环境变量

在 Bash 里,可以通过 export 命令查看当前 Shell 进程的环境变量,这些环境变量一些是 Bash 自己创建的,还有一些是 Bash 从父进程继承来的,然而需要注意的是,父进程传给 Bash 的环境变量不一定是我们想象的那样。

在 C 语言层面,环境变量是存放在一个名为 environ 的全局变量里的,这个变量的值是一个字符串的数组,像这样:

"foo=1", "bar=2"

在父子孙一辈辈进程中传递的就是这么个数组,我们嘴里说的环境变量的原貌其实就是个字符串,而不是我们通常在高级语言里看到的键值对。然而操作系统并没有对环境变量字符串的格式做任何限制,environ 变量的值还可以是这样:

"1=1", "=2", "="

甚至这样:

"foo\nbar=1"

Bash 在启动的时候,会检查 environ 数组中的每个字符串,如果它包含 =,且 = 左边有任意的字符,就把它从 = 分割开,一个做变量名,一个做值,变成自己的变量。咦? "1=1" 也会导入成变量? 是的,在 Bash 的实现当中,是这样的,只是这样的变量会被加上特殊的属性标记。

变量名称合法的变量被添加的属性是: 

att_exported | att_imported

一个表示是要导出给子进程的环境变量,一个表示是从环境变量导入的变量。

变量名不合法的变量被添加的属性是:

att_exported | att_imported | att_invisible

多了一个 invisible 属性,表示不可见。

在 Bash 启动之后,各个内部命令可以通过这个标记判断是不是自己想要的变量。比如 set 命令就不会输出 1 这个变量:

$ env -i 1=1 bash -c set | grep '1='

 

但 export 命令就会:

$ env -i 1=1 bash -c export

declare -x 1 
declare -x OLDPWD
declare -x PWD="/Users/admin"
declare -x SHLVL="1"

declare -x 1 显然是非法的,甚至还可以这样:

$ env -i $'foo\nrm -rf /=' bash -c export

declare -x OLDPWD
declare -x PWD="/Users/admin"
declare -x SHLVL="1"
declare -x foo
rm -rf /

看起来很有风险的样子,不过目前 Bash 4.4 beta 版本,已经修复了这个问题,export 命令不再输出那些变量名不合法的变量了,下面是 export 命令源码做的改动:

+     /* If we imported a variable that's not a valid identifier, don't
+        show it in any lists. */
+     if ((var->attributes & (att_invisible|att_imported)) == (att_invisible|att_imported))
+        continue;

那 Bash 在启动其他程序的时候,会把这样的环境变量传递给子进程吗?答案是会的,我们可以通过在两个 env 命令中间插入一个 bash 命令来看出效果:

$ env -i 1=1 bash -c env

PWD=/Users/admin
SHLVL=1
1=1
_=/usr/bin/env

Bash 在启动其他程序的时候,会把自己所有的变量中带有 att_exported 属性且值不是空的变量以及它们的值分别用 = 连接起来合成一个字符串数组,重新复值给 environ 变量,也就是做了和启动时导入环境变量相反的操作。

那些没有等号或者等号左边没有字符的环境变量,因为 Bash 在启动的时候就丢弃了,所以也就没法传递给它的子进程了。

posted @ 2015-09-14 16:13  紫云飞  阅读(1365)  评论(0编辑  收藏  举报