【Linux】进程概念(五):详解环境变量的本质

常见的环境变量
  • 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
  1. HOME(环境变量):指定用户的主工作目录(用户登录linux系统时,默认的目录)
  2. USER(环境变量):指定当前登录系统的用户名,即操作系统会根据不同的登录用户去对应分配不同的USER环境变量,即不同的环境变量有不同的用户
  3. 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 打印命令行参数
  1. 命令行参数向量表 argv 是程序与命令行环境交互的核心数据结构。这个指针数组以特定的方式组织命令行参数,并在末尾添加了一个重要的结束标记。
  2. 在内存中,假设程序接收了三个命令行参数,那么向量表在内存中的布局将如下所示:argv[0] 指向程序名称字符串,argv[1] 指向第一个实际参数,argv[2] 指向第二个参数,argv[3] 指向第三个参数,而 argv[4] 则是一个 NULL 空指针。这种设计确保了数组下标从 0 到 argc 的范围内都是有效的访问区域,其中 argv[argc] 必定为 NULL 指针。
  3. 这种以 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;
}

在这里插入图片描述

九、环境变量的全局属性
  1. 从理论层面分析,环境变量的全局性建立在进程树模型之上。在操作系统中,所有用户进程都通过父子关系组织成一棵多叉树结构。
  • bash shell 作为用户会话的初始进程,在启动时会从系统配置文件(如 /etc/profile、~/.bashrc 等)中读取并建立初始的环境变量集合。
  • 当 bash 创建子进程时,它会将自己的环境变量副本传递给子进程,这个过程在 fork() 系统调用时自动完成。子进程同样会将环境变量继续传递给自己的子进程,这样就形成了环境变量在整棵进程树中的级联传递。
  • 由于 bash 位于进程树的根部,它定义的环境变量自然能够传播到所有后代进程中,从而实现了环境变量的全局属性。
  1. 为了深入理解这一机制,我们需要区分环境变量本地变量的本质差异。
  • 在 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由于不是环境变量所以没有被继承。

  • 此时我们使用exportbash中的本地变量MYENVE变成环境变量,再使用env | grep MYENV的方式在bash的环境变量中找到MYENV。

在这里插入图片描述

如图此时我们运行自己的程序成功找到MYENV,即子进程继承了fork的环境变量,证明环境变量具有全局属性。

  • 用unset(清除环境变量)将环境变量变成本地变量

在这里插入图片描述


十、常规命令和内建命令

在 Shell 环境中,命令分为两大类型:常规命令和内建命令,它们在执行机制上有着本质的区别。

  1. 常规命令 也被称为外部命令磁盘命令,这类命令的本质是独立的可执行程序文件。当我们在 Shell 中输入一个常规命令时,系统会启动一个新的子进程来执行该命令。Shell 首先通过 PATH 环境变量指定的目录路径来查找对应的可执行文件,找到后使用 fork() 创建子进程,然后通过 exec() 系列函数加载并执行该程序。
相关推荐
世转神风-2 小时前
VMware-挂载报错:no mountpoint specified
linux
专业开发者2 小时前
艾通科技(ITON Technology)借助蓝牙 ® 网状网络,构建适用于自动化控制应用的大规模设备网络
运维·物联网·自动化
KakiNakajima2 小时前
CentOS 7 x86系统安装EMQX 【kaki备忘录】
linux·运维·centos
weixin_462446232 小时前
【原创实践】Docker 镜像批量导出镜像与导入镜像
运维·docker·容器
qq7590353662 小时前
Docker快速部署一款堡垒机系统
运维·docker·容器
敢敢のwings2 小时前
云服务器上部署Dify完整教程
运维·服务器
少年、潜行3 小时前
F1C100/200S学习笔记(1)-- 核心板和验证板硬件设计
linux·驱动开发·f1c200s
东木君_3 小时前
Linux 驱动框架中 Class 机制完整讲解(以 ov13855 摄像头为例)
linux
yiSty3 小时前
linux命令行下使用百度云网盘【自用】
linux·运维·百度云