常见的环境变量
PATH: 指定命令的搜索路径。HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)SHELL: 当前Shell,它的值通常是/bin/bash。
三、查看环境变量的方法
3.1 echo $
echo &NAME:NAME是我们需要查看发环境变量名。

3.2 env
env: 查看所有环境变量。

四、测试PATH
为什么执行ls命令的时候不用带./就可以执行,而我们自己生成的可执行程序必须要在前面带上./才可以执行?

在这里插入图片描述
- 因为要执行一个可执行程序必须要先找到它在哪里。
- 既然不带./就可以执行ls命令,说明系统能够通过ls名称找到ls的位置。
- 而系统是无法找到我们自己的可执行程序的,所以我们必须带上./,以此告诉系统该可执行程序位于当前目录下。
而系统就是通过环境变量PATH来找到ls命令的,查看环境变量PATH我们可以看到如下内容:

在这里插入图片描述
PATH是linux系统的指令默认搜索路径 ,即当我们在bash命令行中输入指令的时候,linux会首先在这个PATH中使用冒号:分隔的路径下逐个进行查找这个指令对应的可执行程序,如果找到了这个指令对应的可执行程序,那么linux就会去执行这个指令,如果没有找到那么会进行报错 而ls命令实际就位于PATH当中的某一个路径下(
/usr/bin),所以就算ls命令不带路径执行,系统也是能够找到的。
代码语言:javascript
AI代码解释
那么我们有没有什么办法能够让自己的程序不带路径也能执行呢?------我们通过两种方法实现
方式一:将可执行程序拷贝到环境变量PATH的某一路径下。
- 既然在未指定路径的情况下系统会根据环境变量PATH当中的路径进行查找,那我们就可以将我们的可执行程序拷贝到PATH的某一路径下,此后我们的可执行程序不带路径系统也可以找到了。
sudo cp 文件名 /usr/bin

在这里插入图片描述
方式二:将可执行程序所在的目录导入到环境变量PATH当中。
- 将可执行程序所在的目录导入到环境变量PATH当中,这样一来,没有指定路径时系统就会来到该目录下进行查找了。
- PATH:添加我们的路径,PATH的作用是查看
PATH=$PATH:(pwd查看的当前路径)

五、测试 HOME & USER
- HOME(环境变量):指定用户的主工作目录(用户登录linux系统时,默认的目录)
- USER(环境变量):指定当前登录系统的用户名,即操作系统会根据不同的登录用户去对应分配不同的USER环境变量,即不同的环境变量有不同的用户
- cd ~进入家目录,这个家目录的位置和HOME指定用户的主工作目录或默认目录相同 ,代表当不同用户登录linux系统的时候会根据不同用户去分配不同的环境变量。
普通用户和超级用户:

在这里插入图片描述
六、环境变量相关命令大全
| 命令 | 语法 | 作用 | 示例 | 作用范围 |
|---|---|---|---|---|
| 显示变量 | echo $VAR | 显示环境变量的值 | echo $PATH | 当前shell |
| printenv | 显示所有环境变量 | printenv | 当前shell | |
| printenv VAR | 显示指定环境变量 | printenv HOME | 当前shell | |
| env | 显示所有环境变量 | env | 当前shell | |
| 设置变量 | VAR=value | 设置局部变量 | NAME="John" | 当前shell |
| export VAR=value | 设置环境变量 | export PATH=$PATH:/bin | 当前shell及子进程 | |
| 修改变量 | export VAR=new_value | 修改环境变量值 | export PATH=/new/path | 当前shell及子进程 |
| VAR=new_value | 修改局部变量值 | NAME="Jane" | 当前shell | |
| 删除变量 | unset VAR | 删除环境变量 | unset TEMP_DIR | 当前shell |
| 变量扩展 | ${VAR} | 变量值扩展 | echo ${PATH} | 当前shell |
| ${VAR:-default} | 空时使用默认值 | echo ${VAR:-"default"} | 当前shell | |
| ${VAR:=default} | 空时设置默认值 | echo ${VAR:="default"} | 当前shell | |
| 查看命令路径 | which cmd | 显示命令的完整路径 | which ls | 当前shell |
| whereis cmd | 显示命令路径及手册页 | whereis python | 当前shell | |
| type cmd | 显示命令类型 | type cd | 当前shell | |
| 特殊变量 | $HOME | 用户家目录 | echo $HOME | 所有shell |
| $PATH | 命令搜索路径 | echo $PATH | 所有shell | |
| $PWD | 当前工作目录 | echo $PWD | 当前shell | |
| $USER | 当前用户名 | echo $USER | 所有shell | |
| $SHELL | 当前shell路径 | echo $SHELL | 所有shell | |
| $PS1 | 主提示符设置 | echo $PS1 | 当前shell | |
| 持久化设置 | 编辑 ~/.bashrc | 用户级环境变量 | vim ~/.bashrc | 永久生效 |
| 编辑 ~/.bash_profile | 登录shell环境变量 | vim ~/.bash_profile | 永久生效 | |
| 编辑 ~/.profile | 用户环境配置 | vim ~/.profile | 永久生效 | |
| 编辑 /etc/profile | 系统级环境变量 | sudo vim /etc/profile | 所有用户 | |
| 编辑 /etc/bashrc | 系统级bash配置 | sudo vim /etc/bashrc | 所有用户 | |
| 生效配置 | source file | 使配置文件立即生效 | source ~/.bashrc | 当前shell |
| . file | 使配置文件立即生效 | . ~/.bashrc | 当前shell | |
| 进程环境 | export -p | 显示所有导出的环境变量 | export -p | 当前shell |
| declare -x | 显示导出的环境变量 | declare -x | 当前shell | |
| set | 显示所有变量和函数 | set | 当前shell | |
| 数组变量 | VAR=(val1 val2) | 定义数组变量 | FILES=(*.txt) | 当前shell |
| echo ${VAR[@]} | 显示数组所有元素 | echo ${FILES[@]} | 当前shell | |
| echo ${#VAR[@]} | 显示数组元素个数 | echo ${#FILES[@]} | 当前shell |
七、 通过代码获取环境变量
7.1 命令行参数
main 函数是 C/C++ 程序的入口点,它通过命令行参数机制为程序提供了与外部环境交互的重要接口。其标准形式为:
代码语言:javascript
AI代码解释
int main(int argc, char* argv[])
参数解析:
argc(参数计数):表示命令行参数的数量,至少为 1(程序名称本身)argv(参数向量):字符指针数组,按顺序存储每个命令行参数字符串的起始地址
底层调用机制: 在程序启动时,main 函数由运行时库的初始化代码(如 Startup() 或 CRTStartup())调用。这些启动例程负责解析命令行输入,并将处理后的参数传递给 main 函数。
参数传递原理: 当在 Linux bash 中执行命令时,系统会将空格分隔的各个字符串解析为独立参数。例如:
代码语言:javascript
AI代码解释
./mycmd -a file.txt
这个命令会产生三个参数:
argv[0]→"./mycmd"argv[1]→"-a"argv[2]→"file.txt"argc的值为 3
指令原型:
代码语言:javascript
AI代码解释
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
// 检查参数数量:程序需要恰好1个参数(不含程序名本身)
if (argc != 2) {
// 显示正确的使用格式提示
printf("用法: %s -[a|b|c]\n", argv[0]);
return 1; // 返回非零值表示执行失败
}
// 输出程序名和用户输入的参数,便于调试和显示
printf("%s[1]->%s ", argv[0], argv[1]);
// 根据不同的命令行选项执行相应功能
if (strcmp(argv[1], "-a") == 0) {
// 处理 -a 选项:执行功能1
printf("功能1\n");
} else if (strcmp(argv[1], "-b") == 0) {
// 处理 -b 选项:执行功能2
printf("功能2\n");
} else if (strcmp(argv[1], "-c") == 0) {
// 处理 -c 选项:执行功能3
printf("功能3\n");
} else {
// 处理未知选项:提示用户输入错误
printf("未知选项\n");
return 1; // 返回非零值表示执行失败
}
// 程序正常执行完成
return 0;
}

在这里插入图片描述
7.2 打印命令行参数
- 命令行参数向量表
argv是程序与命令行环境交互的核心数据结构。这个指针数组以特定的方式组织命令行参数,并在末尾添加了一个重要的结束标记。 - 在内存中,假设程序接收了三个命令行参数,那么向量表在内存中的布局将如下所示:argv[0] 指向程序名称字符串,argv[1] 指向第一个实际参数,argv[2] 指向第二个参数,argv[3] 指向第三个参数,而 argv[4] 则是一个 NULL 空指针。这种设计确保了数组下标从 0 到 argc 的范围内都是有效的访问区域,其中 argv[argc] 必定为 NULL 指针。
- 这种以 NULL 终止的设计带来了多种灵活的遍历方式。开发者可以根据具体需求选择最适合的遍历方法。最直接的方式是利用 argc 参数进行索引循环,通过 for (int i = 0; i < argc; i++) 这样的循环结构逐个访问每个参数。另一种更优雅的方式是直接利用 NULL 终止特性,使用 while (argv[i] != NULL) 作为循环条件。这种方法不需要依赖 argc 参数,代码更加简洁。
代码语言:javascript
AI代码解释
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int cnt = 0;
while (argv[cnt] != NULL)
{
printf("%s[%d]->%s\n", argv[0], cnt, argv[cnt]);
cnt++;
}
return 0;
}

在这里插入图片描述
7.3 命令行第三个参数
char* env[]是main函数的第三个参数,用于接收环境变量表。
- 环境变量指针数组 :
env是一个以 NULL 结尾的字符串指针数组 - 存储内容:每个指针指向一个 "KEY=VALUE" 形式的环境变量字符串
- 系统传递:由操作系统在程序启动时自动填充并传递给程序
- 遍历方式 :与
argv类似,可以通过循环遍历直到遇到 NULL 指针
打印出环境变量表:
代码语言:javascript
AI代码解释
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++)
{
printf("%s\n", env[i]);
}
return 0;
}

在这里插入图片描述
7.4 通过第三方变量environ获取
代码语言:javascript
AI代码解释
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("%s\n", environ[i]);
} r
eturn 0;
}
代码语言:javascript
AI代码解释
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时要用extern声明。

在这里插入图片描述
八、通过系统变量获取或设置环境变量
8.1 getenv
使用代码调用系统调用接口getenv获取PATH环境变量的值和当前登录用户
代码语言:javascript
AI代码解释
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("PATH:%s\n",getenv("USER"));
printf("USER:%s\n",getenv("USER"));
return 0;
}

在这里插入图片描述
九、环境变量的全局属性
- 从理论层面分析,环境变量的全局性建立在进程树模型之上。在操作系统中,所有用户进程都通过父子关系组织成一棵多叉树结构。
- bash shell 作为用户会话的初始进程,在启动时会从系统配置文件(如 /etc/profile、~/.bashrc 等)中读取并建立初始的环境变量集合。
- 当 bash 创建子进程时,它会将自己的环境变量副本传递给子进程,这个过程在 fork() 系统调用时自动完成。子进程同样会将环境变量继续传递给自己的子进程,这样就形成了环境变量在整棵进程树中的级联传递。
- 由于 bash 位于进程树的根部,它定义的环境变量自然能够传播到所有后代进程中,从而实现了环境变量的全局属性。
- 为了深入理解这一机制,我们需要区分环境变量 与本地变量的本质差异。
- 在 bash 命令行中直接通过 VAR=value 形式定义的变量属于本地变量,这类变量仅在当前 shell 进程内部有效,不会传递给任何子进程。
- 本地变量和环境变量是相互独立的概念,各自维护不同的作用域和生命周期。这种设计既保证了系统配置的全局一致性,又为单个进程提供了私有的变量空间。
- export设置一个新的环境变量,unset清除环境变量
- 本地变量可以通过export将自己变为环境变量
- 当本地变量使用export变成环境变量之后,可以通过unset将环境变量再变回本地变量
- 可以使用set查看环境变量和本地变量
- 我们在bash命令行中定义一个本地变量MYENV,接着再使用env显示环境变量,通过管道将数据传输给grep进行过滤MY_VALUE,结果什么都不显示,即无法在环境中找到MYENV
- 我们再使用echo $查看MYNV,查看到MYENV对应的值了555

在这里插入图片描述
- 我们还可以使用set去查看这个MYENV本地变量

在这里插入图片描述
- 我们也通过
set命令显示所有变量后,配合grep进行过滤,可以快速定位到特定的本地变量MYENV。具体命令为:set | grep MYENV。

在这里插入图片描述
- 我们运行的程序,也是fork的子进程,继承fork的环境变量,那么我们使用代码通过getenv测试我们定义的本地变量MYENV是否被继承下来

在这里插入图片描述
结果:运行程序,访问出现错误,子进程中的环境变量中并没有
MYENV,是由于无法我们定义的本地变量MYENV由于不是环境变量所以没有被继承。
- 此时我们使用
export将bash中的本地变量MYENVE变成环境变量,再使用env | grep MYENV的方式在bash的环境变量中找到MYENV。

在这里插入图片描述
如图此时我们运行自己的程序成功找到MYENV,即子进程继承了fork的环境变量,证明环境变量具有全局属性。
- 用unset(清除环境变量)将环境变量变成本地变量

在这里插入图片描述
十、常规命令和内建命令
在 Shell 环境中,命令分为两大类型:常规命令和内建命令,它们在执行机制上有着本质的区别。
- 常规命令 也被称为
外部命令或磁盘命令,这类命令的本质是独立的可执行程序文件。当我们在 Shell 中输入一个常规命令时,系统会启动一个新的子进程来执行该命令。Shell 首先通过 PATH 环境变量指定的目录路径来查找对应的可执行文件,找到后使用 fork() 创建子进程,然后通过 exec() 系列函数加载并执行该程序。