环境变量编程

问题

main 函数 (默认进程入口)

int main(int argc, char* argv[], char* env[])

  • argc - 命令行参数个数
  • argv[] - 命令行参数数组
  • env[] - 环境变量数组 (最后一个元素为 NULL)

什么是环境变量?

环境变量是进程运行过程中可能用到的 "键值对" (NAME = VALUE)

进程拥有一个环境表 (environment list),环境表包含了环境变量

环境表用于记录系统中相对固定的共享信息 (不特定于具体进程)

进程之间的环境表相对独立 (环境表可在父子进程之间传递)

环境表的构成

全局指针 environ 指向当前进程的环境表,环境表是一个字符串数组,数组中的每一个字符串都是一个环境变量,数组的最后最后一项为 NULL,表示环境表的结束

环境变量初探

parent.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

#define EXE "child.out"

int create_process(char* path, char* args[], char* env[])
{
    int ret = fork();
    
    if( ret == 0 )
    {
        execve(path, args, env);
    }
    
    return ret;
}

int main()
{
    char path[] = EXE;
    char arg1[] = "hello";
    char arg2[] = "world";
    char* args[] = {path, arg1, arg2, NULL};
    
    printf("%d begin\n", getpid()); 
    
    printf("%d child = %d\n", getpid(), create_process(EXE, args, args));   
    
    printf("%d end\n", getpid());
    
    return 0;
}

child.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char* argv[], char* env[])
{
    int i = 0;
    
    sleep(1);
    
    printf("process parameter:\n");
    
    for(i=0; i<argc; i++)
        printf("exec = %d, %s\n", 
                       getpid(), argv[i]);
                       
    printf("environment list:\n");
    
    i = 0;
    
    while( env[i] )
        printf("exec = %d, %s\n", 
                       getpid(), env[i++]);
					   
	return 0;
}

parent.c 中会创建子进程,并在子进程中通过 execve(...) 函数运行 child.out 程序,传递给子进程 进程参数 和 环境变量,传递给子进程的环境变量和进程参数的值是一样的

程序运行结果如下图所示:

 通过打印可以看出,子进程的进程参数和环境变量的值是一样的

我们直接在命令行中运行 chlid.out 程序

在命令行中执行 child.out 程序, 打印出来的环境变量是父进程命令行传递过来的,所以 child.out 这个进程的环境变量和命令行的环境变量值是相同的

深入理解环境变量

对于进程来说,环境变量是一种特殊的参数

环境变量相对于 启动参数 较稳定 (系统定义 且 各个进程共享)

环境变量遵守固定规范 (如:键值对,变量名大写)

环境变量 和 启动参数 存储于同一内存区域 (私有)

环境变量读写接口

头文件: #include <stdlib.h>

读: char* getenv(const char* name);

返回 name 环境变量的值,如果不存在,返回 NULL

写: int putenv(char* string);

设置 / 改变 环境变量 (NAME=VALUE),string 不能是栈上定义的字符串

环境表入口:  extern char** environ;

环境变量读写接口初探

parent.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

#define EXE "child.out"

int create_process(char* path, char* args[], char* env[])
{
    int ret = fork();
    
    if( ret == 0 )
    {
        execve(path, args, env);
    }
    
    return ret;
}

int main()
{
    char path[] = EXE;
    char arg1[] = "hello";
    char arg2[] = "world";
    char* args[] = {path, arg1, arg2, NULL};
    
    printf("%d begin\n", getpid()); 
    
    printf("%d child = %d\n", getpid(), create_process(EXE, args, args));   
    
    printf("%d end\n", getpid());
    
    return 0;
}

child.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char* argv[], char* env[])
{
    int i = 0;
    
    sleep(1);
    
    printf("process parameter:\n");
    
    for(i=0; i<argc; i++)
        printf("exec = %d, %s\n", 
                       getpid(), argv[i]);
                       
    printf("environment list:\n");
    
    i = 0;
    
    while( env[i] )
        printf("exec = %d, %s\n", 
                       getpid(), env[i++]);
                       
    printf("original:\n");
    
    printf("%s=%s\n", "TEST1", getenv("TEST1"));
    printf("%s=%s\n", "TEST2", getenv("TEST2"));
    printf("%s=%s\n", "TEST3", getenv("TEST3"));
    
    putenv("TEST1");
    putenv("TEST2=NEW-VALUE");
    putenv("TEST3=CREATE NEW");
    
    printf("new:\n");
    
    printf("%s=%s\n", "TEST1", getenv("TEST1"));
    printf("%s=%s\n", "TEST2", getenv("TEST2"));
    printf("%s=%s\n", "TEST3", getenv("TEST3"));
    
    i = 0;
    
    extern char** environ;
    
    printf("changed:\n");
    
    while( environ[i] )
        printf("exec = %d, %s\n", 
                       getpid(), environ[i++]);
    
    return 0;
}

程序运行结果如下图所示:

parent.out 这个进程创建出一个子进程,并通过 execve(...) 函数,执行 child.out 程序并传递进程参数和环境变量

因为父进程中传递的进程参数和环境变量是一样的,所以 child.out 第 15 行和第 23 行打印出的进程参数和环境变量是一样的

因为子进程当前并不存在名为 TEST1、TEST2、TEST3 的环境变量名,所以第 28 行 - 第 30 行的 getenv(...) 函数返回 NULL

第 32 行和第 34 行,通过 putenv(...) 函数,改变环境变量的值;第 32 行,有环境变量名 TEST1,但是没有环境变量值,这样会清除名为 TEST1 的环境变量;第 33 行和第 34 行会新增   TEST2=NEW-VALUE 和 TEST3=CREATE NEW 的环境变量

第 38 行和第 40 行,我们重新打印名为 TEST1、TEST2、TEST3 的环境变量值,发现新增了 TEST2、TEST3 环境变量

第 48 行 - 50 行,我们通过全局指针 environ,来打印当前进程存在的所有环境变量

编程练习

编写应用程序,通过命令行参数读写环境变量

选项定义

环境变量编程练习

main.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

typedef int (*OptCall)(char* name, char* value);

typedef struct
{
	int opt;
	OptCall handler;
} CallHandler;

static int A_Handler(char* name, char* value)
{
	extern char** environ;
	int ret = 0;
	int i = 0;
	
	while(environ[i])
	{
		printf("%s\n", environ[i++]);
	}
	
	return ret;
}

static int R_Handler(char* name, char* value)
{
	int ret = 0;
	
	if(name == NULL)
	{
		printf("Environ name is NULL!\n");
		ret = -1;
	}
	else
	{
		printf("%s=%s\n", name, getenv(name));
	}
	
	return ret;
}

static int W_Handler(char* name, char* value)
{
	int ret = 0;
	
	if((name == NULL) || (value == NULL))
	{
		printf("Environ name or Environ value is NULL!\n");
		ret = -1;
	}
	else
	{
		char* str = (char*)malloc(sizeof(char) * (strlen(name) + strlen(value) + 2));
		
		if(str)
		{
			strcpy(str, name);
			strcat(str, "=");
			strcat(str, value);
			
			if(putenv(str) == 0)
			{
				printf("New Environ: %s\n", str);
			}
			else
			{
				printf("Error on writing new environ value\n");
				ret = -1;
			}
			
			// free(str); 不能调用 free
		}
		else
		{
			printf("malloc str failed!\n");
			ret = -1;
		}
	}
	
	return ret;
}

static int T_Handler(char* name, char* value)
{
	return W_Handler(name, value) || A_Handler(name, value);
}

CallHandler g_handler[] = 
{
	{'a', A_Handler}, 
	{'r', R_Handler},
	{'w', W_Handler},
	{'t', T_Handler},
};

int g_handler_size = sizeof(g_handler) / sizeof(*g_handler);

int ExecuteOptHandler(int opt, char* name, char* value)
{
	int ret = -1;
	
	for(int i = 0; i < g_handler_size; i++)
	{
		if(opt == g_handler[i].opt)
		{
			ret = g_handler[i].handler(name, value);
		}
	}
	
	return ret;
}

int main(int argc, char* argv[])
{
	int c = 0;
	int opt = 0;
	char* name = NULL;
	char* value = NULL;
	
	while((c = getopt(argc, argv, "arwtn:v:")) != -1)
	{
		switch(c)
		{
			case 'a':
			case 'r':
			case 'w':
			case 't':
				opt = c;
				break;
				
			case 'n':
				name = optarg;
				break;
			
			case 'v':
				value = optarg;
				break;
				
			default:
				exit(-1);
		}
	}
	
	ExecuteOptHandler(opt, name, value);
	
	return 0;
}

我们使用 getopt(...) 函数来匹配命令行参数,根据 opt 来匹配具体的处理函数

第 74 行,不能调用 free(str) 是因为 putenv(str) 会让环境表中的某一项指向 str,并不会再拷贝一份 str,如果我们调用了 free(str),str 存储的内容可能随时被改变

程序运行结果如下图所示:

 程序正常正确的运行了 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值