
|--------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| 作者简要描述 | |
| 算法篇 | Linux篇 |
| QT百日筑基篇 | 数据结构篇 |
环境变量的深度理解
目录/索引
目录
[argc: 命令行参数的个数, 以一个字串为一个基本单位。](#argc: 命令行参数的个数, 以一个字串为一个基本单位。)
[argv: 命令行参数。](#argv: 命令行参数。)
[echo 环境变量 : 用于查看单个环境变量的内容](#echo 环境变量 : 用于查看单个环境变量的内容)
[指令: env](#指令: env)
[指令: export](#指令: export)
[方法二: 第三方变量environ](#方法二: 第三方变量environ)
[方法三: 系统函数getenv(非常推荐)](#方法三: 系统函数getenv(非常推荐))
实训:使用第三种方法写一个只有我当前这个用户才能使用的程序:
基本概念:
环境变量一般是操作系统中用来指定操作系统运行环境的一些参数。
如: 我们在编写C/C++代码的时候,在链接的时候, 我们从不知道链接的动静态库在哪,我们照样可以链接成功, 生成可执行程序, 原因就是相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
深度理解main函数的参数
main函数是有三个参数,这里我们先使用两个。
cpp
1 #include<stdio.h>
2
3 int main(int argc, char* argv[])
4 {
5 for(int i =0; i<argc; i++)
6 {
7 printf("argv[%d]: %s\n",i,argv[i]);
8 }
9 return 0;
10 }
我们编译一下:
makefile文件:
bash
1 code: code.c
2 @gcc -o $@ $^
3 @echo "编译成功"
4
5 .PHONY:clean
6 clean:
7 rm -rf code

我们会发现我们运行出来的是我们刚输入的命令,被切分成了一个个的字串。
所以:
argc: 命令行参数的个数, 以一个字串为一个基本单位。
argv: 命令行参数。
深度解析argv:
我们的argv的类型是一个指针数组, 那就说明,我们在命令行输入的时候,bash会将我们的命令进行切分, 并且将它存储在命令行参数表中。在我们程序中我们可以使用下表的方式进行表中的数据的访问。
命令行参数表存在的意义:
向我们的ls可以携带对应的选项, 形如-l -a -n这样的。
cpp
1 #include<stdio.h>
2 #include<string.h>
3
4 int main(int argc, char* argv[])
5 {
6 if(argc!=2)
7 {
8 printf("请输入选项[a|b|c]\n");
9 return 1;
10 }
11 const char* c = argv[1];
12
13 if(strcmp(c,"-a")==0) printf("执行选项-a\n");
14 else if(strcmp(c,"-b")==0) printf("执行选项-b\n");
15 else if(strcmp(c,"-c")==0) printf("执行选项-c\n");
16 else printf("erro: 无此选项\n");
17 return 0;
18 }
运行结果+测试用例:

总结:
像这样我们可以让我们写的程序携带对应的命令。这样能让我们写的程序进行更多的方法进行写入。一个程序可以通过不同的选项来实现不同指令的子功能, 命令行参数更像是这一块的地基用来实现这种子功能。
浅聊看环境变量:
我们要执行一个成序就要先找到它, 系统中的环境变量可以帮助我们进行查找,他能帮助我们找到对应的命令。我们的ls这种命令可以不带路径,那么就是底层的环境变量帮我们找到了对应的路径,在的文件夹是在/usr/bin目录下,我们可以进行对应的查找。

那我们将我们的代码拷贝到对应的路径是不是我们也能不带命令进行对应的操作了???
我们的必须要带sudo为什么呢?因为我们的/usr/bin的拥有者和所属组都是root所以我们要进行提权处理。
bash
ls -al /

bash
sudo cp ./code /usr/bin
发现我们可以不进行路径的携带就能进行对应的运行了。但是放入里面会不安全,我们不进行对应的操作,我们code并不是很常用,下次换个文件名便使用不了了。

cpp
sudo rm /usr/bin/code
删除/usr/bin路径下的code
win
在我们windows也有对应的环境变量, 我们有的时候也要进行对应的配置

环境变量++PATH++深度理解:
在我们的系统中存在很多的环境变量像我们经常使用的PATH化境变量。
我们可以简单的进行查看这个环境变量:
这就是我们系统所存在的一些环境变量,是我们启动程序所自带的一些环境变量。
echo $环境变量 : 用于查看单个环境变量的内容
bash
echo $PATH

那我们是不是将我们对应的路径 添加进去是不是我们不要带对应的路径就可以进行运行了。
我们尝试一下:
在PATH中配置环境变量
覆盖试的写法
bash
环境变量名=路径
PATH=路径
我们发现我们的code没有携带路径就能直接的进行运行,但是我们之前的ls mkdir这样的命令是不能进行运行了的,他找不到对应的命令了,pwd是内建命令,所以环境变量不影响它。

拯救的方法:
1.复制之前的路径,再次进行复制操作。
bash
PATH=/home/zb/.npm-global/bin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

2.关掉机器再次的进入,他就会将我们的PATH进行重置。
他是内存级的命令,每次登录我们Linux机器时都会在家目录下进行对应的拷贝。
我们可以简单的进行查看:


对应一些环境变量的配置

2.我们进行添加对应的环境变量(非覆盖试的添加)
PATH=$PATH=路径
以:为分割的路径。这是后我们就能运行当前目录下的所有程序可不携带路径。
bash
PATH=$PATH:路径


我们发现我们也可以不带路径进行对应的操作。
其他环境变量:
指令: env
作用:查看系统中所有的环境变量。

首先:
PWD: 记录的是我们当前的路径。
HOME=/home/zb 是我们的工作目录即:家目录。
USER=zb 是我们当前机器使用的用户。
SHELL=/bin/bash 当前的Shell,一般都是bash。
LS_COLORS: 当前Linux的着色器。
SSH_CONNECTION: 登录的主机地址ip。
OLDPWD=/home/zb:上一次的路径"和我们cd -"指令息息相关。
PATH: 环境变量PATH
TERM=xterm: 终端命令
SSH_TTY=/dev/pts/2 当前的机器号。我这打开了多个程序因此这里是2否则这里是"1".
HISTSIZE=1000:历史粗存储的命令最大的数量(我们上下箭头查找历史命令就是使用了这个环境变量)我们的Ctrl+R查询历史命令也与这个环境变量有关。
使用su进行用户的切换:

使用su- 进行用户的切换:

结论:
su只是改变用户名, su -:是当前用户名的用户进行重新的登录。
当然我们也能通过下面这种方式进行查看此环境变量的内容。

指令: export
作用设置一个新的环境变量
bash
export 环境变量名="变量"
//注意复制前后不要有空格

也可以先定义一个变量
bash
变量="值"
export 变量

那么如何理解环境变量?存储角度
bash会形成一张表: 环境变量表, 他的类型是char*\[\] ,当然内部的数据可能是malloc...动态申请出来的可以近似的理解成为一个二维数组。
最后以NULL来进行结尾。

我们的env指令就是打印这张表。
和环境变量相关的命令:
echo: 显示某一个环境变量的内容。
export:设置一个新的环境变量
env:查看系统的所有环境变量。
unset:清除某一个环境变量。
bash
unset 环境变量名
我们发现下面没有对应的环境变量了。

如何通过代码来获取环境变量
方法一:main的第三个参数
env:是记录环境中的环境变量他的类型是char* 。
cpp
1 #include<stdio.h>
2 #include<string.h>
3
4 int main(int argc, char* argv[], char* env[])
5 {
6 (void)argc;//这两行相当于给这两个参数一个事干, 定义出来不使用可能会报错,所以我们强转一下
7 (void)argv;
8 for(int i=0;env[i];i++)
9 printf("env[%d]->%s\n",i,env[i]);
10 return 0;
11 }

这个环境变量是父进程的,可以被子进程进行继承处理。我们重新定义一个环境变量,再次运行也能拿到,这就说名我们的子进程的环境变量是可以继承父进程的。

当然我们的c语言最先执行的不是main函数而是_start函数
他大概的内实现是:
cpp
_start
(
int a=0;
if(a=0) main();
else if(a==2) main(argc,argv);
else if(a==3) main(argc,argv,env);
else erro;
)
方法二: 第三方变量environ
bash
man environ

解释一下这里为什么是一个二级指针:
因为我的environ是一个指针要指向环境变量表中的位置我的,表是一个指针类型的,所以我要用二级指针来指向一级指针。
扩展:为什么我们可以environ【i】这样来进行访问???
environi=*(environ+i);
cpp
1 #include<stdio.h>
2 #include<string.h>
3
4 extern char** environ; //使用前要进行前置的声明
5
6 int main(int argc, char* argv[], char* env[])
7 {
8 (void)argc;
9 (void)argv;
10 (void)env;
11
12 for(int i=0;environ[i];i++)
13 printf("%s\n",environ[i]);
14
15 return 0;
16 }

方法三: 系统函数getenv(非常推荐)

我们模拟输出一个USER吧
cpp
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 extern char** environ;
5
6 int main()
7 {
8
9 printf("%s\n", getenv("USER"));
25 return 0;
26 }

总结:
这三种方法都能获得环境变量,但是第三种方式它更加的灵活,便与我们控制,前两种更偏向大印所有的环境变量,想打印单个的话你得知道环境变量表的位置。
putenv也能进行对应的操作
实训:使用第三种方法写一个只有我当前这个用户才能使用的程序:
cpp
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 extern char** environ;
5
6 int main()
7 {
8
9 if(strcmp("zb",getenv("USER"))==0)
10 {
11 printf("能进行程序的执行\n");
12 }
13 else
14 {
15 printf("不能进行程序的执行\n");
16 }
33 return 0;
34 }
我们的zb能正常的执行,而我们的root不能执行。

我们能通过环境变量来进行一些程序的定制,也能禁止一些程序的运行
环境变量具有全局性
环境变量具有全局特性, 可以被子进程给进程下去。
我们上面使用了在命令行中定义变量,这种变量叫做本地变量。

我们想要查看本地变量:
指令set
作用:打印所有的本地变量和环境变量
bash
set | head -100
打印的太多了我只要前100行

shell脚本
创建一个.sh为后缀的文件
bash
vim add.sh
必须以这个进行开头:
bash
#! /bin/bash
下面写要进行执行的命令:

运行命令:
bash
bash add.sh
就会执行里面的命令:

本篇完
下篇预告: 进程地址空间
