Linux -- 环境变量与命令行参数
一、环境变量
1、概念
2、命令行参数
# main
函数其实是有参数的,但是我们一般并不是经常使用。以下是main
函数的原型:
int main(int argc,char* argv[],char* env[])
- argc:代表命令行有效参数的个数。
- argv : 指向命令行参数。
- env: 指向环境变量。
# main函数也是要被调用的,因为操作系统还会在main函数之前对我们的程序做修改,所以我们的main函数是有参数的。agrv:指针数组每个指针指向一个字符串,agrc:指针数组的数组大小。
# 我们看下面这段代码,我们输出argv的每个字符串:
#include
// main函数有参数吗?
int main(int argc, char *argv[])
{
for(int i = 0; i < argc; i++)
{
printf("argv[%d]:%s
", i, argv[i]);
}
return 0;
}
# 我们./code a b c d执行程序后,发现他就会打印出来的就是我们的命令行命令以空格为分隔,填充到agrv中。
# 一共有五个有效参数,第一个有效参数为./code
,第二个有效参数为a
,第三个参数为b,
第四个参数为c,
第五个参数为d。这五个有效参数都被argv
这个指针数组所指向,并且argv
最后一个参数指向NULL
。
# 我们再看一段代码:
#include
#include
// main函数有参数吗?
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Uasge:%s [-a|-b|-c]
", argv[0]);
return 1;
}
const char *arg = argv[1];
if(strcmp(arg, "-a") == 0)
printf("这是功能1
");
else if(strcmp(arg, "-b") == 0)
printf("这是功能2
");
else if(strcmp(arg, "-c") == 0)
printf("这是功能3
");
else
printf("Uasge:%s [-a|-b|-c]
", argv[0]);
return 0;
}
# 我们这里根据main函数的参数输出不同的语句,直接./code就提示我们命令行需要加上选项,./code -a/-b/-c就输出不同的信息,所以我们main的参数叫做命令行参数,他是实现程序不同子功能的方法。
# 我们平时的指令也是一个程序,他的main函数可以带命令行参数,就可以支持我们的指令+选项执行不同的功能。由于命令行指令是被bash切分再填充到argv的数组里面的,所以我们的进程启动时默认就有一张argv表叫做命令行参数表。
# main函数的第三个参数,建议看完目录6.5之后,回头再看。
# 同样我们通过以下代码来探究一下第三个参数:
#include
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(i=0; env[i]; i++)
{
printf("[%d]->%s
", i, env[i]);
}
return 0;
}
3、理解PATH
# 那为什么我们平时写代码main函数没有参数呢?因为我们的程序功能就一个,跑起来就结束了,而我们有时候希望程序可以有不同的子功能,就可以设置命令行参数让程序具有不同的子功能。
# 我们执行平时指令不需要./,可以直接Is,但是我们的自己写的程序需要./,这是为什么呢?我们都知道,指令是一个二进制程序,我们写的也是二进制程序,他们没有本质区别。但是我们都知道要找到一个程序需要先找到他,所以我们的程序需要./,是因为在当前目录下要找到文件,而我们系统的指令不需要,因为有环境变量帮助他寻找。
# 所以是不是把我们程序拷贝到系统的/usr/bin目录下就可以不带./了呢?是的。同时因为系统命令是root的,所以我们需要sudo提权,此时我们就可以直接code执行了。但是我们不建议把自己的二进制文件拷贝到系统目录中,因为我们的程序没有经过严格的检测和发布流程,可能存在bug,此时就可以能污染系统的指令池。
# 我们可以通过env指令查看全部环境变量。
# 我们的系统存在PATH环境变量,PATH环境变量就是用来告诉我们系统到那些目录找文件,可以看到我们的PATH存在很多个目录,互相以 : 为分割,环境变量 = 名字 + 内容。环境变量是变量,所以我们可以echo $环境变量名查看,注意 $ 不可以忽略,否则就是直接输出环境变量名了。
# 我们的系统就会在PATH环境变量里面根据绝对路径+文件名打开的方式,从左到右查找,全部查找之后都没有就报错,说明不存在,找到了就直接加载文件。所以此时我们也可以就修改PATH变量,把我们的路径添加上去之后,就可以直接code执行了。但是此时我们Is这指令就执行不了,此时再去查看PATH,就发现我们的PATH之前的路径都被覆盖了。那该怎么办?不用担心,PATH是一个内存级变量,我们重新登录PATH就直接恢复了。
4、环境变量的组织方式
# 每个程序都会收到⼀张环境变量表,环境变量表是⼀个字符指针数组,每个指针指向⼀个以’ ’结尾的环境变量字符串。
# 接下来我们举个例子,从存储的角度理解一下环境变量:
# 我们的环境变量的值是保存在bash的上下文的数据里的,进程启动时,bash读取所有环境变量信息,同时bash内部也会形成两张表,一张叫做环境变量表,一张叫做命令行参数表,都是一个指针数组。
# 我们执行ls a b命令,命令先被bash拿到,切分、填充形成argv命令行参数表,然后再到保存到环境变量表里面,找到PATH路径,由PATH到系统查找,存在就创建子进程,不存在就报错。环境变量表就是环境变量的字符串指针数组。
# 所以环境变量是存在bash的上下文里面的。环境变量就是key_value的长字符串。env就是在打印环境变量表的内容。
# 可是刚开始的时候,我们的进程没启动时,就没有bash,那环境变量有没有呢?
# 环境变量刚开始是存在系统的相关配置文件里面的,我们的进程启动后,bash就从配置文件里面读取环境变量,然后创建环境变量表,这个相关的环境变量配置文件就是家目录下的.bashrc和.bash_profile文件。
# bash启动时就会根据.bashrc和.bash_profile文件的内容构建环境变量,所以我们把自己路径添加到.bash_profile配置文件里面之后,我们重新登陆xshell,此时我们就可以直接执行code打印环境变量就可以看到我们添加的目录。
5、常见环境变量
# logname:我们使用 su 指令时,user不会改变还是hzy,su- 才会改变,因为 su 只是让我们把权限提升为root,但是su - 相当于root重新登陆。
# oldpwd:记录上一次的路径所以我们cd- 就可以切回到上一次所处目录。
# 总结:环境变量就是操作系统运行环境的一些参数,而我们参数就被我们的bash使用,相当于间接的被用户使用。
6、获取环境变量的指令
6.1 env
# 指令env
:显示所有的环境变量。
6.2 echo $环境变量名
# echo $环境变量名:查看环境变量的内容。
6.3 export
# 指令export
:设置一个新的环境变量。
6.4 set
# 指令set
:显示本地定义的shell
变量和环境变量。
6.5 unset
# 指令unset
:取消本地变量与环境变量。
# 导入环境变量可以使用export,取消环境变量可以使用unset,本质都是对环境变量表的增加和删除。
6.6 main函数的env[]参数
#include
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(i=0; env[i]; i++)
{
printf("[%d]->%s
", i, env[i]);
}
return 0;
}
# 环境变量会被子进程继承,也会被子进程后面的子进程继承。
6.7 getenv()
# getenv()系统调用:
它能根据名称获取对应的环境变量。
# 今天我们想写一个只有我们能执行的程序,其他人都不可以执行,包括root。所以我们可以直接获取当前USER,与hzy字符串对比,如果不是,我们就不让执行匹配就正常执行。
#include
#include
#include
int main(int argc, char *argv[], char *env[])
{
(void)argc;
(void)argv;
(void)env;
const char *who = getenv("USER");
if(who == NULL) return 1;
if(strcmp(who, "hzy") == 0)
{
printf("这是程序的正常执行逻辑
");
}
else
{
printf("Only hzy!
");
}
return 0;
}
# 为什么要让子进程继承环境变量呢?
# 因为子进程可以根据环境变量进行个性化操作。比如让进程只有当前用户能执行!
# 我们还可以导入一个flag环境变量,然后flag为1时候可以执行进程,为0就不可以执行,此时我们就可以通过设置flag进而控制子进程是否执行。注意这里我们要void应用一下,否则gcc会报错。同时如果有子进程要修改环境变量那环境变量也会发生写时拷贝 。
6.8 environ
# 第三方变量environ:直接
获取环境变量。
# 我们系统里面还存一个environ的二级指针的全局变量,他就是指向环境变量表的指针所以是二级指针,所以我们这里声明一下environ,然后直接通过environ打印环境变量表的内容也可以拿到环境变量。
#include
int main(int argc, char* argv[])
{
extern char **environ; // 先声明外部变量
for(int i = 0; environ[i]; i++)
{
printf("%s
", environ[i]);
}
return 0;
}
# 小实验:做⼀下~/.bash_profile && ~/.bashrc,修改⽂件级环境变量