本篇目标:
理解环境变量,熟悉常见环境变量及相关指令
一.环境变量
1.概念
引子:
我们可能会有这样的疑问:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找,当然今天讲的并不是动静态库,以后会讲的,那什么是环境变量呢?
环境变量(environmentvariables)⼀般是指在操作系统中用来指定操作系统运行环境的⼀些参数,但是光是这样说,肯定是不明不白地,下面就由浅入深的讲解它。
2.命令行参数
先别急着了解环境变量,可以先了解一下命令行参数。
2.1.概念
命令行参数 就是你在启动程序时,直接在程序名称后面输入的附加信息,例如我们常用的ls -l中的-l就是命令行参数,而ls 就是命令本身,下面用一个我们可能没见过的c语言代码演示一下
cpp
#include <stdio.h>
int main(int argc, char* argv[])
{
//...
return 0;
}
main函数其实也是有参数的,而参数中的argc其实就是个计数器,用来记录程序的命令行参数总数
它永远至少是 1, 因为即使没输入任何参数,程序本身的名字也会占用一个位置。
argv是一个指向变长字符串数组的指针,每个字符串代表一个命令行参数,在程序启动时,操作
系统会将命令行字符串拆分,存储在进程的用户栈空间内,并由argv指向这些地址,如果没有命令
行字符串,那么argv[0]就默认是程序本身的名字
代码演示:
code.c中:
cpp
#include <stdio.h>
int main(int argc, char* argv[])
{
for (int i = 0; i < argc; i++)
{
printf("%s ", argv[i]);
}
printf("\n");
return 0;
}
makefile中:
cpp
code:code.c
gcc -o $@ $^ -std=c99
PHONY:clean
clean:
rm -rf code
输出结果:
可以看出,当我们输入额外的-a/-b/-c时,额外的-a/-b/-c都会被填进到argv所指向的变长字符串数
组中,所以假设我们输入./code -a -b -c时,就会创建这个表,如图:
那么是谁在做这件事呢?答案就是bash,Bash 将我们输入的每一个字符以空格分隔来依次存入了这张表中,下面用代码来加深我们的理解:
cpp
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if(argc!=2)
{
printf("参数不匹配\n");
}
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-a") == 0)
{
printf("[匹配成功] 索引 argv[%d]: 检测到 -a,执行 A 逻辑。\n", i);
}
else if (strcmp(argv[i], "-b") == 0)
{
printf("[匹配成功] 索引 argv[%d]: 检测到 -b,执行 B 逻辑。\n", i);
}
else if (strcmp(argv[i], "-c") == 0)
{
printf("[匹配成功] 索引 argv[%d]: 检测到 -c,执行 C 逻辑。\n", i);
}
else
{
printf("[未知参数] 索引 argv[%d]: %s,程序跳过。\n", i, argv[i]);
}
}
return 0;
}
输出结果:
可以看出,我们通过在./code后面加上-a/-b/-c就可以实现打印特定的内容,这是不是与我们的命令后面的命令+命令行参数(选项)十分十分的类似,所以我们可以得出下面的结论:
bash会为每个进程创建一张argv表,用来支持实现选项功能
可是这又有另外一个问题:我们执行./code时,是在当前目录下执行这个程序,但是ls -al在当前目录下并没有啊,他又是如何执行的呢?
3.环境变量
我们在界面按下env即可查看所有的环境变量,如图:

下面讲解一下PATH
3.1.PATH环境变量
操作echo $PATH,即可打印当前的搜索路径列表,如图:

作用:提供一套自动搜索可执行文件的目录清单。
例如我们在终端输入一个不带路径的命令(如 ls )时,系统会按照 PATH 中列出的目录顺序,从
左到右依次查找是否存在同名的可执行文件。
所以我的意思就是如果将我们可执行程序添加到这个表里面。那么当我们想要运行code时,就不
需要./code了,可以直接code,下面我来演示一下:

虽然将/home/kong/code_git/fufu直接粗暴的添加到了PATH里面,但是也导致了对PATH的覆盖,
不过也不用担心,我们直接退出,重新连接即可恢复。
此时,我就可以在这里输出一个结论:当我们进入程序时,bash会为我们创建一个环境变量表,bash会将env所呈现的所有的环境变量添加到这个表里面。
但是这也引出了,另外一个问题:环境变量最开始从哪里来的呢?这就不得不提到系统的相关的配置文件了。
3.2.配置文件
其实我们在/root下还有许多的隐藏目录/文件,如图:

今天主要讲.bashrc与.bash_profile,先打开这两个文件,如图:
.bash_profile文件:
./bashrc文件:
在打开/etc/bashrc文件:
注意:if里面的**.** 就是告诉 Bash:不要只运行那个文件,要把那个文件里的所有变量定义都搬到我当前的内存里来,额/etc/bashrc中也存在环境变量。
结论:.bash_profile向./bashrc中拿环境变量,./bashrc向/etc/bashrc中拿环境变量。
所以如果我们将我们想要运行的程序放到这个配置文件时,当我们重连接时,就可以直接运行了。
4.认识更多的环境变量
<1>.HOSTNAME代表的时是你当前连接的 Linux 服务器的主机名
<2>.HISTSIZE代表的是当前程序最多能保存的执行过的命令,我们也可以通过history来查看我们的历史命令。
<3>.USER 和 LOGNAME 都是 Linux 系统中标识用户的环境变量,绝大多数直接登录的场景下两者值相同。
<4>.PWD是表示 Linux 系统中当前工作目录环境变量
<5>.HOME表示的是Linux 系统中当前用户的主目录(家目录)环境变量,例如我们在root权限下,HOME=/root,在kong普通用户下,HOME=/home/kong,所以当我们操作cd ~时,~就会被替换为HOME。
5.获取环境变量
5.1.命令
<1>.env: 显示所有环境变量
<2>.echo:显⽰某个环境变量值
<3>.export: 设置⼀个新的环境变量,例如:

export PATH=$PATH:可执行文件所在的绝对路径,即可添加新的环境变量
<4>.unset: 清除环境变量
<5>.set: 显示本地定义的shell变量和环境变量
5.2.代码
**5.2.1.**命令行第三个参数(env)
cpp
#include <stdio.h>
int main(int argv,char*argc[], char *env[])
{
for( int i = 0;; env[i]; i++)
{
printf("env[i]->%s\n",i, env[i]);
}
return 0;
}
输出结果:

如果我们通过export来创建环境变量,例如export MYENV1=11111,export MYENV2=22222,export MYENV3=33333,在env就如图所示:

可以看出我们自己创建的环境变量导入到了bash里面,此时在运行code,就会如图所示:

所以我们可以得出结论:父进程的环境变量可以被子进程继承,所有的进程都可以拿到环境变量,所以这也可以证明环境变量具有全局性。
5.2.2.通过第三方变量environ
cpp
#include<stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++)
{
printf("environ[%d]->%s\n",i, environ[i]);
}
return 0;
}
5.3.3.通过getenv获取
cpp
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
如果我们通过export来创建环境变量,例如export MYENV1=11111,export MYENV2=22222,export MYENV3=33333,在env就如图所示:

可以看出我们自己创建的环境变量导入到了bash里面,
此时,我们就可以创建一个只可以当前用户可以使用的程序,例如:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
// 1. 从环境变量表中获取当前执行者的用户名
char *current_user = getenv("USER");
// 2. 核心逻辑:比对是否为 "kong"
// 如果环境变量里根本没有 USER,或者名字不是 "kong"
if (current_user == NULL || strcmp(current_user, "kong") != 0) {
printf("权限拒绝:该程序仅限用户 [kong] 执行。\n");
printf("当前尝试运行的用户是: %s\n", (current_user ? current_user : "未知"));
return 1; // 异常退出
}
// 3. 验证通过后的逻辑
printf("验证通过!欢迎回来,kong。\n");
printf("正在加载您的私人配置...\n");
// 可以在这里添加你只想让自己运行的功能代码
return 0;
}