你好,我是安然无虞。 |
文章目录
- 自学网站
- 基本概念
- 常见环境变量
- 相关命令
- 获取环境变量
- 环境变量·全局属性
自学网站
基本概念
在谈环境变量的基本概念之前呢,我们先看下面这段程序:
//test.cpp文件下
#include<stdio.h>
int main()
{
printf("Hello Linux.\n");
return 0;
}
我们自己定义的Makefile文件:
mytest:test.cpp
g++ test.cpp -o mytest
.PHONY:clean
clean:
rm -f mytest
make之后生成可执行程序 mytest,我们运行看看效果:
很明显报错了,报错说没有想找到,如果像下面这样改动呢?
我们看到,带上路径之后运行成功了,上面没带路径却报没有找到的错误,这是为什么呢?而且我们知道,像ls, pwd, touch…这些系统指令,实际上也是可执行程序,为什么它们不用带路径也能直接执行呢?
有了上面的失败经验,我们知道,执行一个可执行程序,前提是要找到它。
为什么系统的命令能找到,而我们自己的程序找不到呢?
因为系统中是存在相关的环境变量,保存了程序的搜索路径的。系统中搜索可执行程序的环境变量叫做PATH(像ls, pwd……这些指令可以在PATH中找到)
所以,环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,比如我们在编写C/C++代码,在链接的时候,从来不知道我们所链接的动静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找;其次就是环境变量通常具有全局属性。
说到这里可能有老铁会好奇,怎么将我们自己生成的可执行程序 mytest 能像系统指令一样,不用带上路径也可以直接执行呢?
有两个方法:
方法1:
将 mytest 拷贝进 /usr/bin 路径下(极不推荐,易污染)
说明:系统的所有指令都在 /usr/bin 路径下。
//拷贝
sudo cp mytest /usr/bin
//删除
sudo rm /usr/bin/mytest
方法2:
将我们的程序所在的路径加入到环境变量PATH当中,export PATH=$PATH:mytest程序所在路径。
先查看程序当前路径:
执行下面命令:
export PATH=$PATH:/home/sl/code/linux-programming/TestProcess
常见环境变量
常见的环境变量有:
1.PATH:指定命令的搜索路径
2.HOME:指定用户的主工作目录
3.SHELL:当前SHELL,它的值通常是/bin/bash
相关命令
1.echo:显示某个环境变量值
echo $NAME //NAME为环境变量名称
2.export:设置一个新的环境变量
注意哦,命令行上是可以定义变量的哟。
val1=12; //定义变量val1,初始化为12
说明:命令行变量分为两种:
- 普通变量(如val1)
- 环境变量——全局属性(如testVal)
3.env:查看环境变量
4.set:显示本地定义的shell变量和环境变量
5.unset:清除环境变量
获取环境变量
在讲解获取环境变量之前,请老铁回答我一个问题:我们之前使用C语言时,经常写main函数,所以我要问的是main函数可以带参数吗,最多可以带几个?
按照我们以前的学习,可能我们会认为是可以带两个,好的,请看下面代码:
#include<stdio.h>
int main(int argc, char* argv[])
{
for(int i = 0; i < argc; i++)
{
printf("argv[%d] : %s\n", i, argv[i]);
}
return 0;
}
好,下面我们保存退出到命令行中运行:
argc 是 argv 数组的元素个数.
上面的 ./mytest 是程序名(可执行程序), -a -b -c -d 这样的字符串是选项.
我们给 main 函数传递的是 argc 和 argv数组, 而命令行参数传递的是, 命令行中输入的程序名和选项 .
这样传的意义是什么呢? 同一个程序, 通过传递不同的参数, 让同一个程序有不同的执行逻辑, 执行结果.
Linux 系统中会根据不同的选项, 让不同的命令, 可以有不同的表现, 指令中那么多选项的由来, 和起作用的方式.
好,下面我们做一个小实验:
写一个命令行计算器:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char* argv[])
{
if(argc != 4)
{
//使用手册: 如./mycal -a 10 20
printf("Usage : %s [-a|-s|-m|-d] one_data two_data\n", argv[0]/*程序名*/);
return 0;
}
int x = atoi(argv[2]);//atoi()是将字符串转变成成整数
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;
}
好, 有了上面的铺垫之后, 想必老铁对于命令行参数有了一个新的认知, 再回到之前抛出的问题, main函数最多可以带几个参数, 其实是3个!
好,下面我们总结一下通过代码获取环境变量的三种方式:方式一 : 命令行第三个参数
其实我们的每一个程序都会收到一张环境表, 环境表示一个字符指针数组, 每个指针指向一个以 ‘\0’ 结尾的环境变量字符串.
#include<stdio.h>
//每个进程都会被传入环境变量参数的
int main(int argc, char* argv[], char* env[]/*第三个参数*/)
{
int i = 0;
for(i = 0; env[i] != NULL; i++)
{
printf("%s\n", env[i]);
}
return 0;
}
执行程序:
方式二 : 通过第三方变量environ获取
全局变量 environ 指向环境变量表, environ 没有包含在任何头文件中, 所以在使用时, 要用 extern 声明.
#include<stdio.h>
int main(int argc, char* argv[])
{
//环境变量的指针声明
extern char** environ;
int i = 0;
for(i = 0; environ[i] != NULL; i++)
{
printf("%s\n", environ[i]);
}
return 0;
}
方式三 : 调用getenv函数
第三种方式是调用 getenv 函数获取指定的环境变量.
比如:
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
环境变量·全局属性
环境变量通常具有全局属性, 可以被子进程继承下去.
我们知道, 命令行中启动的进程, 父进程全部都是bash.
为什么环境变量具有全局属性呢?
因为环境变量要被子进程继承下去.
所谓的本地变量(不带export), 本质就是在 bash 内部定义的变量, 不会被子进程继承下去.
代码验证一下:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
while(1)
{
printf("hello world, pid:%d, ppid:%d, myenv:%s\n", getpid(), getppid(), getenv("test_sl"));
sleep(1);
}
return 0;
}
首先设置 test_sl 为本地变量:
此时运行程序查看结果:
下面我们将 test_sl 设置成环境变量:
此时运行程序查看结果:
好的,上面的实验结果就说明了环境变量具有全局属性, 请老铁细品哦.
还有一个问题:
创建一个本地变量 local_val, 但是我们发现 echo 也是一条命令, 要通过子进程的方式执行, 与我们上面所说的"本地变量不会被子进程继承下去"相悖.
解释:
Linux下大部分命令都是通过创建子进程的方式执行的, 但是, 还有一部分命令, 不通过创建子进程的方式执行, 而是由 bash 自己执行(调用自己对应的函数来完成特定的功能), 我们把这种命令叫做内建命令.