文章目录
- 环境变量的引入
- 环境变量
- 环境变量概念
- 环境变量的特性以及命令行操作
- 本地变量
环境变量的引入
main参数(命令行参数)
先来看看这样的代码以及运行结果:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char *argv[])
{
int i = 0;
for(i = 0;i<argc;i++) # for(i = 0;argv[i];i++)
{
printf("argv[%d]:%s\n",i,argv[i]);
}
return 0;
}
运行结果:
原理:
在Linux中," [root@iZ2vcb2izu72dt7ghpv46fZ Test]# " 这个字符串是bash进程输出的,后面我们跟的 字符串 ./myprocess a b c d (结合上图)相当于我们在做输入, 如下图代码所示,bash首先输出一个字符串,然后等着用户做输入,将输入的 ./myprocess a b c d 这个长的字符串放入到buffer里面,输入过后被命令行解释器bash读取到了,命令行解释器会帮我们做一件工作,将这个大字符串以空格为分隔,将大字符串分隔开,放到一个char* 类型的数组,这里叫做char* argv[ ] (如下图),bash除了会维护一个char*数组,还会维护一个int类型的数据,记录该数组里面的字符串个数,这里用int argc表示;这样,就可以拿到了命令行输入的5个参数(字符串) 了;
printf("[root@iZ2vcb2izu72dt7ghpv46fZ Test]#");
char buffer[1024];
scanf("%s",buffer); //fgets();
总结:
在命令行上输入的以空格作为分割符的字符串,会被bash解析成对应的一个一个的小子串,然后维护成一个指针数组,再通过一定的方式,将参数传下去;
为什么要这么做呢?
我们可以通过不同的选项,让我们的同一个程序执行它内部不同的功能。就比如Linux向命令行解释器输入不同的指令,例如ls -a ls -l等等。
命令行参数是命令行选项的基础。
看一个demo代码进行解释:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
//实现一个版本控制 实现三个版本 输入可执行程序+版本号
int main(int argc,char *argv[])
{
if(argc!=2) //如果参数不是两个
{
printf("Usage\n\t%s -number[1-3]\n",argv[0]);//提示输入
return 1;
}
if(strcmp("-1",argv[1])==0) //版本一
{
printf("function 1\n");
}
else if(strcmp("-2",argv[1])==0)//版本二
{
printf("function 2\n");
}
else if(strcmp("-3",argv[1])==0) //版本三
{
printf("function 3\n");
}
else{
printf("Unkown\n");
}
return 0;
}
运行结果:
基于上面的代码实现一个命令行计算器:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
// ./myprocess -[ADD|Mul|Sub|Div] x y
int main(int argc,char *argv[])
{
if(argc!=4){
printf("Usage\n\t%s -[Add|Mul|Sub|Div] x y\n",argv[0]);
return 1;
}
int x = atoi(argv[2]);
int y = atoi(argv[3]);
if(strcmp("-Add",argv[1])==0)
{
printf("%d\n",x+y);
}
else if(strcmp("-Mul",argv[1])==0)
{
printf("%d\n",x*y);
}
else if(strcmp("-Sub",argv[1])==0)
{
printf("%d\n",x-y);
}
else if(strcmp("-Div",argv[1])==0)
{
if(y==0)
printf("除0错误\n");
else
printf("%d\n",x/y);
}
else{
printf("Unkown\n");
}
return 0;
}
运行结果:
环境变量
环境变量概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
思考:为什么我们要运行我们所写的可执行程序的时候需要带./ ?而在执行系统指令的时候却不需要./ ?
因为在系统当中存在一个全局的环境变量,名字叫PATH。可以echo $PATH查看环境该环境变量(如下图):
- 在我们的系统里面,有一块内存空间,里面保存了一个字符串(就是对应上图的路径),名字叫PATH,然后我们在执行任何命令的时候,都会在这个路径下展开搜索,这个路径时由很多个子路径构成,冒号分隔,其中系统命令都在这个路径中的某个子路径下面,而我们所写的程序没有在上面字符串中,所以不加./系统找不到该二进制可执行程序,所以需要./告诉系统在当前路径。
如果想要自己写的程序不带./就能运行。
- 第一种方法:将我们的可执行程序拷贝到上面的目录里面
- 第二种方法:可以将该二进制程序文件路径加上去
指令:
# PATH=路径:$PATH //路径是你要加的路径
注意:当shell退出后,会自动将这个路径从PATH中移除。
查看系统中的所有环境变量,env+回车:
示例:
思考:为什么在使用操作系统的时候,操作系统一直认识你?
因为当启动xshell的时候,已经把你这个用户在登录时所需要维护的相关数据暂时都给你保存起来了,当需要访问时,系统想要知道你是谁,用户名等信息可以通过环境变量去找,在每一次登录系统都会在内存中开一个空间,将你这个用户的环境变量存储起来。
环境变量的特性以及命令行操作
- 查看环境变量
# env
- 查看单个环境变量
# echo $PATH
- 定义一个环境变量
# export NAME=XXJ //export后面+需要定义的环境变量
- 获取环境变量
上面我们看到了main函数可以又两个参数,其实main函数最多可以有三个参数,第三个参数也是一个char*的数组,也是以NULL结尾的,
测试代码:
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char* argv[],char* env[])
{
for(int i = 0;env[i];i++)
{
printf("env[%d]:%s\n",i,env[i]);
}
return 0;
}
运行结果:根据运行结果可以看出:env里面存储的是指向环境变量的指针。
- bash进程会维护两张表,一张是命令行参数那张表(char* argv[]),另一张为环境变量(char* env[]),当bash在调用main函数的时候,会将下面表的起始地址传给main函数的第三个参数,这时对应的进程也就拿到了父进程的环境变量。所以得出结论:环境变量默认是被子进程继承下去的。
测试代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main(int argc,char* argv[],char* env[])
{
printf("-----------------------------------------------\n");
printf("i am father pid:%d ppid:%d\n",getpid(),getppid());
for(int i = 0;env[i];i++)
{
printf("env[%d]:%s\n",i,env[i]);
}
pid_t id = fork();
if(id==0)
{
printf("-----------------------------------------------\n");
printf("i am child pid:%d ppid:%d\n",getpid(),getppid());
for(int i = 0;env[i];i++)
{
printf("env[%d]:%s\n",i,env[i]);
}
}
sleep(1);
return 0;
}
运行结果:
- 环境变量具有全局属性。
- 环境变量会被子进程,包括孙子进程所继承。
如果不写参数,怎么获取环境变量呢,也就是不用继承的方式,怎么获取环境变量?
函数getenv获取函数变量里面的内容(根据环境变量的名字)
用法:getenv(环境变量名)
如下图:
测试代码:
int main()
{
char* username = getenv("USER");
if(username!=NULL)
printf("username:%s\n",username);
else
printf("None\n");
return 0;
}
运行结果:
代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
char* username = getenv("USER");
if(strcmp("root",username)==0)
{
printf("this is my process core function\n");
}
else{
printf("you don't have authotity\n");
}
return 0;
}
运行结果:可以写身份识别的代码,只有特定身份才能执行对应的代码,其他身份不能执行。
通过environ获取环境变量
environ是一个二级指针,指向的就是环境变量的那张表(char* env[ ])。
测试代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
extern char** environ;
for(int i = 0;environ[i];i++)
{
printf("environ[%d]:%s\n",i,environ[i]);
}
return 0;
}
运行结果:
本地变量
当我们在命令行直接输入NAME=XXJ,我们就定义了一个本地变量;
变量名为NAME,变量的内容就是XXJ;
示例:
set命令
set命令可以把几乎所有的变量都显示出来,包含环境变量与本地变量。
注意:本地变量不是环境变量,本地变量只在bash进程有效,不能被继承。
示例:
测试代码:
提示:上面的测试过程中,NAME=XXJ是一个环境变量;
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
const char* str = getenv("NAME");
if(str==NULL)
{
printf("get None\n");
}
else{
printf("%s\n",str);
}
return 0;
}
运行结果分析:因为NAME=XXJ是环境变量,所以会被继承下去,所以str能拿到NAME的内容。
测试代码二:
提示:HELLO=LLLLLLLLLL是本地变量
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5
6 int main()
7 {
8 const char* str = getenv("HELLO");
9 if(str==NULL)
10 printf("get None\n");
11 else{
12 printf("%s\n",str);
13 }
14
15 return 0;
16 }
运行结果分析:HELLO是一个本地变量,本地变量不会被继承,所以在子进程中获取不到。
注意:删除掉本地变量与环境变量unset指令
# unset 变量名
问题:bash的子进程的环境变量从bash继承的,那bash没有父进程,那它的环境变量在哪里拿的呢?
bash登陆后,环境变量都是内存级的,都是从磁盘文件读来的,当bash进程退出了,环境变量(进程中的)自然而然就没有了(磁盘的还在)。
每一个用户在登录是都有自己的一套环境变量,在家目录里面的一个.bash_profile文件中。(如图)
可以用这个配置欢迎语哦:
步骤:打开家目录下的.bash_profile文件,加一个字符串,如下图所示:
效果展示: