7.9 环境变量
如同前述,环境字符串的形式是:
name = value
UNIX内核并不关心这种字符串的意义,它们的解释完全取决于各个应用程序。例如, shell使用了大量的环境变量。其中某一些在登录时自动设置(如HOME,USER等),有些则由用户设置。我们通常在一个shell起动文件中设置环境变量以控制shell的动作。例如,若设置了环境变量M A I L PAT H,则它告诉Bourne shell和K o r n Shell到哪里去查看邮件。
ANSI C定义了一个函数getenv,可以用其取环境变量值,但是该标准又称环境的内容是由实现定义的。
#include <stdlib.h>
char *getenv(const char *name) ;
//返回:指向与name关联的value的指针,若未找到则为NULL
注意,此函数返回一个指针,它指向name = value字符串中的value。我们应当使用getenv从环境中取一个环境变量的值,而不是直接存取environ。
POSIX . 1和X P G 3定义了某些环境变量。表7 - 1列出了由这两个标准定义并受到SVR4和4 . 3 + BSD支持的环境变量。SVR4和4 . 3 + BSD还使用了很多依赖于实现的环境变量。FIPS 151-1要求登录shell必须要定义环境变量HOME和LOGNAME。
表7-1 环境变量
变量 说明
HOME 起始目录
L A N G 本地名
L C _ A L L 本地名
L C _ C O L L A T E 本地排序名
L C _ C T Y P E 本地字符分类名
L C _ M O N E T A R Y 本地货币编辑名
L C _ N U M E R I C 本地数字编辑名
L C _ T I M E 本地日期/时间格式名
LOGNAME 登录名
N L S P A T H 消息类模板序列
P A T H 搜索可执行文件的路径前缀表
T E R M 终端类型
T Z 时区信息
除了取环境变量值,有时也需要设置环境变量,或者是改变现有变量的值,或者是增加新的环境变量。(在下一章将会了解到,我们能影响的是当前进程及其后生成的子进程的环境,但不能影响父进程的环境,这通常是一个shell进程。尽管如此,修改环境表的能力仍然是很有用的。)不幸的是,并不是所有系统都支持这种能力。
修改环境表三个函数的原型是:
#include <stdlib.h>
int putenv(const char *str) ;
int setenv(const char *name, const char *value, int rewrite) ;
//两个函数返回:若成功则为0,若出错则为非0
void unsetenv(const char *name) ;
这三个函数的操作是:
• putenv取形式为name = value的字符串,将其放到环境表中。如果name已经存在,则先删除其原来的定义。
• setenv将name设置为value。如果在环境中name已经存在,那么( a )若rewrite非0,则首先删除其现存的定义;( b )若rewrite为0,则不删除其现存定义(name不设置为新的value,而且也不出错)。
• unsetenv删除name的定义。即使不存在这种定义也不算出错。
这些函数在修改环境表时是如何进行操作的呢?回忆一下图7 - 3,其中,环境表(指向实际name = value字符串的指针数组)和环境字符串典型地存放在进程存储空间的顶部(栈之上)。删除一个字符串很简单——只要先找到该指针,然后将所有后续指针都向下移一个位置。但是增加一个字符串或修改一个现存的字符串就比较困难。栈以上的空间因为已处于进程存储空间
的顶部,所以无法扩充,即无法向上扩充,也无法向下扩充。
l 如果修改一个现存的name:
n 如果新value的长度少于或等于现存value的长度,则只要在原字符串所用空间中写
入新字符串。
n 如果新value的长度大于原长度,则必须调用malloc为新字符串分配空间,然后将新字符写入该空间中,然后使环境表中针对name的指针指向新分配区。
l 如果要增加一个新的name,则操作就更加复杂。首先,调用malloc为name = value分配空间,然后将该字符串写入此空间中
n 如果这是第一次增加一个新name,则必须调用malloc为新的指针表分配空间。将原来的环境表复制到新分配区,并将指向新name = value的指针存在该指针表的表尾,然后又将一个空指针存在其后。最后使environ指向新指针表。再看一下图7 - 3,如果原来的环境表位于栈顶之上(这是一种常见情况),那么必须将此表移至堆中。但是,此表中的大多数指针仍指向栈顶之上的各name = value字符串。
n 如果这不是第一次增加一个新name,则可知以前已调用malloc在堆中为环境表分配了空间,所以只要调用realloc,以分配比原空间多存放一个指针的空间。然后将该指向新name = value字符串的指针存放在该表表尾,后面跟着一个空指针。