环境变量(Environment variable)一般指在操作系统中用来指定操作系统运行环境的一些参数。
例如我们编写的代码在链接的时候,我们并不知道要链接的动态库静态库在哪里,但是照样可以链接成功,原因就是有相关的环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,在系统中通常具有全局特性。
❓为什么我们的代码运行要带路径,而系统的指令不用带路径?
输入 env
查看所有环境变量。
系统中搜索可执行程序的环境变量叫做 PATH
[CegghnnoR@VM-4-13-centos ~]$ env | grep PATH
LD_LIBRARY_PATH=:/home/CegghnnoR/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/CegghnnoR/.local/bin:/home/CegghnnoR/bin
也可以通过 echo $PATH
查看:
[CegghnnoR@VM-4-13-centos ~]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/CegghnnoR/.local/bin:/home/CegghnnoR/bin
PATH 内有多个路径,以 :
作为分隔符。
当我们输入指令后,就会在这些路径中一个一个地搜索,直到找到该指令的可执行程序。
而我们自己写的程序,不在PATH的路径中,所以要自己带上路径。
❓如何能让自己写的程序运行也不带路径呢?
我们当然可以把自己的可执行程序粘贴到 PATH 的某个路径的底下,不过要在 root 下或者 sudo。我们不建议这样做。
可以在 PATH 下增加新的路径,export PATH=$PATH:[绝对路径]
,这样该路径下的可执行程序运行就不用带路径了。
PATH 改错了没关系,退出重新登陆就恢复了。
MANPATH
:man 手册的搜索路径
HOSTNAME
:主机名
SHELL
:当前Shell,它的值通常是 /bin/bash
HISTSIZE
:能保存的历史命令条数
USER
:当前用户名
MAIL
:邮箱路径
PWD
:当前所处路径
LANG
:当前采用的编码格式
HOME
:当前用户对应的家目录
LOGNAME
:登陆用户
echo
:显示环境变量
export
:设置一个新的环境变量,或者将本地变量导出为环境变量
env
:显示所有环境变量
unset
:清除环境变量
set
:显示本地定义的 shell 变量和环境变量
在命令行中不加 export
直接定义的变量叫做本地变量,它是局部的。
我们知道 main
函数可以带两个参数:int argc, char* argv[]
第二个参数是一个指针数组,第一个参数表示这个数组的大小。
它们被称作命令行参数,传递的是命令行中输入的程序名和选项。
我们可以打印里面的内容看一下:
#include
#include
int main(int argc, char* argv[])
{
for (int i = 0; i < argc; ++i)
{
printf("argv[%d]:%s\n", i, argv[i]);
}
return 0;
}
结果:
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
argv[0]:./mytest
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest -a
argv[0]:./mytest
argv[1]:-a
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest -a -b -c
argv[0]:./mytest
argv[1]:-a
argv[2]:-b
argv[3]:-c
argv
数组的最后一个以 NULL
结尾。
利用这个,我们可以实现一个命令行上的计算器:
#include
#include
#include
#include
int main(int argc, char* argv[])
{
if (argc != 4)
{
printf("Usage: %s [-a|-s|-m|-d] [one_data] [two_data]\n", argv[0]);
return 0;
}
int x = atoi(argv[2]);
int y = atoi(argv[3]);
if (strcmp("-a", argv[1]) == 0)
{
printf("%d + %d = %d\n", x, y, x + y);
}
else if (strcmp("-s", argv[1]) == 0)
{
printf("%d - %d = %d\n", x, y, x - y);
}
else if (strcmp("-m", argv[1]) == 0)
{
printf("%d * %d = %d\n", x, y, x * y);
}
else if (strcmp("-d", argv[1]) == 0 && y != 0)
{
printf("%d / %d = %d\n", x, y, x / y);
}
else
{
printf("Usage: %s [-a|-s|-m|-d] [one_data] [two_data]\n", argv[0]);
}
return 0;
}
结果:
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal
Usage: ./mycal [-a|-s|-m|-d] [one_data] [two_data]
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -a 21 34
21 + 34 = 55
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -s 21 34
21 - 34 = -13
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -m 21 34
21 * 34 = 714
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -d 28 4
28 / 4 = 7
那么我们讲这个的意义是什么呢?
通过这个,我们可以意识到:Linux系统中,同一个命令给与不同的选项会有不同的表现,这也是指令中这么多选项的由来和起作用的方式。
其实 main
函数还可以带第三个参数 char* env[]
,它表示的就是环境变量
#include
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 显示出来的是一样的。
这说明:一个进程是会被传入环境变量参数的。
C语言细节:一个没有参数列表的函数是可以被传入参数的,只不过未被使用。如果参数列表显式写上 void
则无法传参。
除了上面获得环境变量的方式,我们还可以这样:
#include
int main()
{
extern char** environ;
for (int i = 0; environ[i]; ++i)
{
printf("%d: %s\n", i, environ[i]);
}
return 0;
}
第三种获得环境变量的方式:
直接使用 getenv()
函数
#include
#include
int main()
{
char* val = getenv("PATH");
printf("%s\n", val);
return 0;
}
因为环境变量中的值是有意义的。
比如我们可以通过USER环境变量来控制程序的运行权限:
下面这个程序,只有我能运行 if 语句后面的部分,其他用户都不行(甚至连 root 也不行,除非他改我代码):
#include
#include
#include
int main()
{
char* id = getenv("USER");
if (strcmp(id, "CegghnnoR") != 0)
{
printf("权限拒绝!\n");
return 0;
}
printf("成功执行\n");
return 0;
}
我们知道,命令行中启动的进程,其父进程都是 bash
如果我们随便获取一个不存在的环境变量:
#include
#include
#include
int main()
{
printf("Hello world, pid:%d, ppid:%d, myenv:%s\n", getpid(), getppid(), getenv("nihao"));
return 0;
}
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
Hello world, pid:30517, ppid:26920, myenv:(null)
结果为null
CegghnnoR@VM-4-13-centos 2022_8_14]$ nihao=11112222333
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
Hello world, pid:30921, ppid:26920, myenv:(null)
创建一个本地变量,结果依然为null
[CegghnnoR@VM-4-13-centos 2022_8_14]$ export nihao
[CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
Hello world, pid:31480, ppid:26920, myenv:11112222333
将它导出为环境变量,可以成功显示环境变量的值。
这说明,环境变量是会被子进程继承下去的。
而本地变量,本质就是在 bash 内部定义的变量,不会被子进程继承下去。
残留问题
定义的本地变量可以使用 echo 打印,export 导出
[CegghnnoR@VM-4-13-centos 2022_8_14]$ nihao=22
[CegghnnoR@VM-4-13-centos 2022_8_14]$ echo $nihao
22
[CegghnnoR@VM-4-13-centos 2022_8_14]$ export nihao
❓echo 和 export 都是命令,不也是子进程吗,那么它们是怎么获得本地变量的呢?
其实 Linux 下大部分命令都是通过子进程的方式执行的,
但是,还有一部分命令,不通过子进程的方式执行,而是由 bash 自己执行(调用自己对应的函数来完成)。
我们把这种命令叫做内建命令。