首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Linux】进程概念(五):详解环境变量的本质

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

作者头像
我不是呆头
发布2025-12-20 14:01:32
发布2025-12-20 14:01:32
20
举报

引言

在Linux系统的广阔天地中,环境变量如同无形的神经网络,默默构建着程序运行的生态基础。它们以全局属性的特质贯穿整个进程体系,通过精妙的继承机制将配置信息从父进程传递到子进程,形成一张覆盖所有应用的环境网络。理解环境变量的工作原理,就如同掌握了一把开启系统运行机制的钥匙,让我们能够深入探索命令行背后的奥秘,解密程序执行的环境依赖,从而真正驾驭操作系统的核心力量。

一、基本概念

环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,环境变量通常具有某些特殊作用,同时它还具有全局属性

例如:我们在编写c/c++代码的时候,在进行链接的时候,从来不知道我们所链接的动静态库在哪里,但是依旧可以链接成功,生成可执行程序,原因就是有相关的环境变量帮助编译器进行查找

二、常见的环境变量

  • 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
复制
那么我们有没有什么办法能够让自己的程序不带路径也能执行呢?——我们通过两种方法实现

方式一:将可执行程序拷贝到环境变量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
复制
int main(int argc, char* argv[])

参数解析:

  • argc(参数计数):表示命令行参数的数量,至少为 1(程序名称本身)
  • argv(参数向量):字符指针数组,按顺序存储每个命令行参数字符串的起始地址

底层调用机制: 在程序启动时,main 函数由运行时库的初始化代码(如 Startup()CRTStartup())调用。这些启动例程负责解析命令行输入,并将处理后的参数传递给 main 函数。

参数传递原理: 当在 Linux bash 中执行命令时,系统会将空格分隔的各个字符串解析为独立参数。例如:

代码语言:javascript
复制
./mycmd -a file.txt

这个命令会产生三个参数:

  • argv[0]"./mycmd"
  • argv[1]"-a"
  • argv[2]"file.txt"
  • argc 的值为 3

指令原型:

代码语言:javascript
复制
#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
复制
#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
复制
#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
复制
#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
复制
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时要用extern声明。
在这里插入图片描述
在这里插入图片描述

八、通过系统变量获取或设置环境变量

8.1 getenv

使用代码调用系统调用接口getenv获取PATH环境变量的值和当前登录用户

代码语言:javascript
复制
#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() 系列函数加载并执行该程序。

常见的如 ls、grep、find 等都属于常规命令,它们以二进制文件或脚本的形式存储在文件系统的特定目录中。每个常规命令的执行都会产生一个新的进程,执行完毕后该进程终止,控制权返回给 Shell 父进程。

  1. 内建命令是 Shell 程序自身的组成部分,它们的代码直接编译在 Shell 的可执行文件中。当执行内建命令时,Shell 不会创建新的子进程,而是在当前 Shell 进程的地址空间内直接执行相应的函数代码。这种执行机制使得内建命令具有更高的执行效率,因为它们避免了创建新进程的开销。

常见的内建命令包括 cd、export、unset、echo 等,这些命令通常用于改变 Shell 自身的状态或环境。例如 cd 命令需要改变当前 Shell 的工作目录,如果作为外部命令实现,子进程改变自己的工作目录后无法影响父进程 Shell 的当前目录。

  • 内建命令通常用于执行与 Shell 环境密切相关的操作,如变量设置、目录切换、作业控制等。
  • 而常规命令则提供更通用的功能,如文件操作、文本处理、系统管理等。用户可以使用 type 命令来查询某个命令的类型,例如 type cd 会显示"cd is a shell builtin",而 type ls 则会显示 ls 的完整路径信息。

理解这两种命令的区别对于深入掌握 Shell 编程和系统管理具有重要意义,特别是在编写脚本时能够根据需求选择合适的命令类型。

总结

环境变量是操作系统中具有全局属性的运行参数,通过进程继承机制在整个系统中传播。bash作为所有用户进程的父进程,其环境变量会被所有子进程继承,从而实现全局效应。环境变量与仅当前shell有效的本地变量不同,可通过export命令提升为环境变量。系统通过PATH环境变量查找命令,解释了为何系统命令无需路径而自定义程序需要。程序可通过main函数参数、getenv函数或environ全局变量获取环境变量。Shell命令分常规命令(外部可执行文件)和内建命令(Shell内置功能),后者直接修改Shell状态且效率更高。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
    • 一、基本概念
    • 二、常见的环境变量
    • 三、查看环境变量的方法
      • 3.1 echo $
      • 3.2 env
    • 四、测试PATH
    • 五、测试 HOME & USER
    • 六、环境变量相关命令大全
    • 七、 通过代码获取环境变量
      • 7.1 命令行参数
      • 7.2 打印命令行参数
      • 7.3 命令行第三个参数
      • 7.4 通过第三方变量environ获取
    • 八、通过系统变量获取或设置环境变量
      • 8.1 getenv
    • 九、环境变量的全局属性
    • 十、常规命令和内建命令
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档