在 Bash 里,能够经过 export 命令查看当前 Shell 进程的环境变量,这些环境变量一些是 Bash 本身建立的,还有一些是 Bash 从父进程继承来的,然而须要注意的是,父进程传给 Bash 的环境变量不必定是咱们想象的那样。数组
在 C 语言层面,环境变量是存放在一个名为 environ 的全局变量里的,这个变量的值是一个字符串的数组,像这样:bash
{"foo=1", "bar=2"}
在父子孙一辈辈进程中传递的就是这么个数组,咱们嘴里说的环境变量的原貌其实就是个字符串,而不是咱们一般在高级语言里看到的键值对。然而操做系统并无对环境变量字符串的格式作任何限制,environ 变量的值还能够是这样:ide
{"1=1", "=2", "="}
甚至这样:spa
{"foo\nbar=1"}
Bash 在启动的时候,会检查 environ 数组中的每一个字符串,若是它包含 =,且 = 左边有任意的字符,就把它从 = 分割开,一个作变量名,一个作值,变成本身的变量。咦? "1=1" 也会导入成变量? 是的,在 Bash 的实现当中,是这样的,只是这样的变量会被加上特殊的属性标记。操作系统
变量名称合法的变量被添加的属性是: code
att_exported | att_imported
一个表示是要导出给子进程的环境变量,一个表示是从环境变量导入的变量。blog
变量名不合法的变量被添加的属性是:继承
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 1 显然是非法的,甚至还能够这样:
$ env -i $'foo\nrm -rf /=' bash -c export declare -x OLDPWD |
看起来颇有风险的样子,不过目前 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 |
Bash 在启动其余程序的时候,会把本身全部的变量中带有 att_exported 属性且值不是空的变量以及它们的值分别用 = 链接起来合成一个字符串数组,从新复值给 environ 变量,也就是作了和启动时导入环境变量相反的操做。
那些没有等号或者等号左边没有字符的环境变量,由于 Bash 在启动的时候就丢弃了,因此也就无法传递给它的子进程了。