当前位置: 首页 > news >正文

Linux----环境变量

环境变量

环境变量就是操作系统为进程提供的一组“键值对”形式的配置信息。

形式上像这样:

也就是说,环境变量是一种:

  • 全局性的

  • 键值对形式的

  • 可被进程读取的配置参数


环境变量的作用

环境变量主要用于:

  1. 控制程序行为(如 LANG 控制语言环境)

  2. 指定系统路径(如 PATH 决定可执行文件搜索路径)

使用getenv函数可以得到对应字段的值:

与echo指令输出的一样:

也可以为PATH变量加一些内容:注意要使用$PATH:连接后面的内容,如果直接PATH=.....就会导致覆盖原来PATH的内容。这个修改只是当前 shell 临时生效,所以你关闭当前终端、重新打开新的终端,PATH 会恢复成登录时的默认值。

同样,在linux下可以打印USER环境变量,每一个用户对应一个USER,对应一个家目录

举个例子 👇
当你在终端输入:

python3

系统其实会去找 PATH 环境变量:

PATH=/usr/local/bin:/usr/bin:/bin

它会依次在 /usr/local/bin/usr/bin/bin 目录下找 python3 文件,找到就运行。


三、环境变量与进程的关系

在 Linux(或类 Unix 系统)中:

每个 进程都有自己的一份环境变量表

  • 当你登录一个用户时,系统会根据该用户的配置文件加载一组环境变量(如 .bashrc.profile)。

  • 当你运行一个新程序时,父进程(如 shell)会拷贝一份自己的环境变量表传给子进程。

也就是说:

不是全系统共享一个表,而是每个进程都有自己的一份拷贝。


四、用户与环境变量的关系

你问得非常好:“一个用户对应一组环境变量吗?”

回答是:

✅ 是的,一个用户登录后,系统会为他加载特定的一组环境变量。
但这组变量只是该用户的默认配置,不同进程可以修改自己的副本。

举例说明:

用户环境变量 HOME环境变量 PATH
userA/home/userA/usr/local/bin:/usr/bin:/bin
userB/home/userB/usr/local/bin:/usr/bin:/bin
  • 用户 A 登录 → 拥有属于自己的环境变量表

  • 用户 B 登录 → 也有自己的环境变量表

  • 二者互不影响


五、环境变量在进程切换中的情况

当操作系统在 并发执行多个进程(比如 A 和 B)时:

  • 每个进程保存自己的一份环境变量;

  • 在进程切换时,操作系统会恢复该进程的寄存器状态、内核栈、内存映射等

  • 这份环境变量表也属于进程用户空间的数据;

  • 切换回该进程时,它仍能看到自己那份变量表(比如 $HOME, $PATH 等)。

环境变量是进程上下文的一部分(位于用户空间),随进程独立存在。

导入新的环境变量

可以为环境变量导入新的值:

注意不要写成:这样是会使VALUE变成shell的本地变量,不是环境变量,用set本地变量可以查看。set可以查看所有 shell 中定义的变量(本地变量 + 环境变量 + 函数定义)。

本地变量与环境变量的对比:最重要的一点就是环境变量可继承,而本地变量不可继承。

对比项本地变量(Local Variable)环境变量(Environment Variable)
定义方式A=10export A=10A=10; export A
可见范围当前 shell当前 shell + 子进程
存放位置shell 的本地符号表shell 的环境表(可继承)
查看命令setenv
是否自动传递给子进程❌ 否✅ 是
删除方式unset Aunset A

还有一个有意思的点,就是定义同名的变量,本地变量与环境变量定义先后的区别:

本地变量先定义:ABCD会变成环境变量

去掉环境变量ABCD后:调用set后出现_=ABCD。在 Bash 中,_ 是一个特殊变量,用来保存 最近一次执行命令的最后一个参数或最近使用的变量名

环境变量先定义:此后再使用ABCD赋值,仅仅是对ABCD修改值,并不会影响它环境变量的地位

取消ABCD环境变量地位:

操作结果是否传递给子进程
A=1创建本地变量❌ 否
export A=1创建环境变量✅ 是
A=2(在已 export 后)修改环境变量的值✅ 是
export -n A取消 export 标记,变成本地变量❌ 否

在 Shell 中,变量名唯一,环境变量只是“带 export 标志”的普通变量。如果你重新定义它,它会被覆盖,但仍然保持 export 状态。

命令行参数

第一个被执行的程序:启动程序(启动器)

在大多数操作系统(如 Linux)中:

  1. 操作系统内核在用户按下运行命令或 shell 调用程序时,比如./a.out时,会:

    • 加载可执行文件到内存;

    • 创建进程控制块(PCB);

    • 初始化堆栈、堆、寄存器等。

  2. **启动代码(CRT,C Runtime)**被执行:

    • 初始化程序执行环境

      • 设置栈、堆、全局变量、静态变量;

      • 初始化 .bss 段(未初始化全局变量置 0);

      • 初始化 .data 段(已初始化全局变量)。

    • 处理命令行参数和环境变量

      • 把操作系统提供的命令行参数和环境变量整理成 argcargvenvp 形式。

    • 调用构造函数(C++)

      • 如果是 C++ 程序,会调用全局对象的构造函数(__libc_init_array)。

    • 调用用户的 main 函数

      • 传入 argcargvenvp

      • 获取返回值。

Linux 下 CRT 的典型实现

在 Linux(glibc)下,启动过程通常是:

程序入口 _start → crt1.o → crti.o → crtn.o → main()
  • _start

    • 汇编实现,是程序真正的入口(ELF 文件入口地址);

    • 做最底层的初始化,比如栈指针、寄存器。

  • crt1.o / crti.o / crtn.o

    • 链接时加入,包含初始化代码;

    • 调用 _libc_start_main

  • _libc_start_main(glibc 提供)

    • 调用所有初始化函数;

    • 然后调用用户的 main(argc, argv, envp)

    • 再调用 exit(main返回值)

main函数中的三个参数

int main(int argc, char *argv[], char *envp[])
| 参数名  | 全称                | 类型    | 作用            |
| ------ | ------------------- | ------- | ------------- |
| `argc` | argument count      | int     | 命令行参数的数量      |
| `argv` | argument vector     | char*[] | 命令行参数字符串数组    |
| `envp` | environment pointer | char*[] | 环境变量字符串数组(可选) |

argc和argv

运行这段代码,可以看的出来,argv的第一参数是程序名字,后面的依次是选项。

让我们联想到平时使用的指令,不同的选项可以有不同的功能,我们来模拟实现一下。

可以做一个类似指令的程序:

可以做的更逼真一些:

所以,main的这两个参数的意义就是为指令、工具、软件等提供命令行选项的支持!

envp

envp 参数的意义就是 让 C/C++ 程序在启动时直接访问子进程的环境变量表,它是操作系统传给程序的环境快照。

如何理解上面这句话,看如下例子:在当前 shell 中执行 ./a 时,环境变量到底是怎么从父进程传给子进程的。


一、执行 ./a 时的参与者

假设你现在在 bash 里:

$ ./a

涉及的两个进程:

  1. bash —— 父进程

  2. ./a —— 子进程(由 bash 创建并执行)


二、bash 执行命令的内部步骤

当你在 shell 输入 ./a 时,bash 主要经历以下过程:

阶段调用说明
1️⃣ 创建子进程fork()bash 调用 fork() 创建一个新进程,复制自身所有内存空间(包括环境变量)
2️⃣ 执行新程序execve()子进程调用 execve("./a", argv, envp) 启动新的可执行文件
3️⃣ 载入程序内核接手内核加载 ELF 程序到内存,并把 argcargv[]envp[] 压栈传给它
4️⃣ 程序运行_startmain()C 运行时初始化,调用用户的 main 函数

三、环境变量的传递过程详解

(1)bash 保存了所有环境变量

bash 进程中有一个环境变量表,比如:

PATH=/usr/bin:/bin
HOME=/home/wzz
LANG=en_US.UTF-8

它存在 bash 进程的用户空间中(在 environ 全局变量中)。


(2)fork() 阶段

fork() 复制整个进程的地址空间:
子进程获得了一份 bash 的环境变量副本

验证一下,子进程的环境变量来自父进程:

在 bash 中设置环境变量TEST,./c.out执行时 bash fork + execve("./c.out", argv, environ),bash 调用 fork() → 创建一个子进程;子进程调用 execve("./c.out", argv, environ);这里 environ 就是 bash 的环境变量表,包括 TEST=521;内核把这份表复制到新程序的栈中。所以子进程的环境变量来自父进程。

内建命令和常规命令

一、基本定义

类型名称执行位置举例
内建命令Built-in command由 Shell 自身实现并在当前进程中执行cd, echo, export, pwd, set, exit, alias
外部命令(常规命令)External / Regular command由文件系统中的独立可执行文件执行(通过 fork + exec)ls, cat, grep, cp, mv, python, gcc

二、区别详解

对比项内建命令外部命令
实现位置Shell 程序本身实现(如 bash 的源码里)独立的可执行文件,通常在 /bin/usr/bin 等目录下
执行方式当前 Shell 进程 中执行(不创建新进程)Shell 会 fork 一个子进程exec 对应的程序
执行效率无需新进程,速度更快需要系统调用 fork/exec,略慢
对环境变量的影响可以直接修改当前 Shell 环境(例如 cd, export只影响自己的子进程环境,对当前 Shell 无影响
是否可被替换/卸载不可卸载(属于 Shell 的一部分)可以替换、删除或重命名
查找顺序Shell 优先检查是否为内建命令若不是内建命令,再在 PATH 中查找外部命令

三、为什么 cd 必须是内建命令?

举个例子:

cd /home/user

如果 cd 是外部命令,那么执行时会创建子进程,
这个子进程改变自己的工作目录没问题,但它一退出,父进程(当前 Shell)仍然在原来的目录。
所以你表面上执行了 cd,实际上没效果。

模拟实现一下:chdir可以改变当前进程的工作目录。

使用ls观察该进程的工作目录,cwd就是当前进程的工作目录。proc存放着当前运行的进程。可以看到30秒后,切换到了家目录。

因此,cdexportaliasunset 等必须是 内建命令,因为它们需要改变 当前 Shell 环境

四、如何区分内建与外部命令

可以用以下命令:

type 命令名

示例:

$ type cd
cd is a shell builtin$ type ls
ls is /bin/ls
http://www.dtcms.com/a/486193.html

相关文章:

  • 高可用架构实战:SpringBoot+MongoDB构建AI原生应用
  • 新疆维吾尔自治区第一届“丝路杯”网络安全大赛暨2026年新疆职业院校技能大赛网络安全赛项竞赛样题
  • 自定义层和读写文件
  • SQL Server 2019实验 │ 存储过程和触发器的使用
  • Font Awesome 方向图标详解
  • 在源码之家下载的网站模板可以作为自己的网站吗怎么提升网站流量
  • MySQL客服端工具
  • ElementUi【饿了么ui】
  • 五点法求解相机的相对位姿
  • 外贸网站推广工作哈尔滨建站
  • 网站右边跳出的广告怎么做dw网站建设基本流程
  • Excelize 开源基础库发布 2.10.0 版本更新
  • iOS 26 系统流畅度测试实战分享,多工具组合辅助策略
  • 智尚房产中介小程序
  • Kuboard突然各种proxy访问401解决
  • 自己做卖假货网站小程序怎么制作开发
  • 专业网站优化山西省城乡住房建设厅网站首页
  • 后端Node知识框架图(Node、Express、KoaNest)
  • 数据结构3:线性表2-顺序存储的线性表
  • TaskIQ 是什么,怎么做异步任务
  • 服务器CPU达到100%解决思路
  • 在 Claude Code 中设置 MCP 服务器(技术总结)
  • 网站上传根目录如何制作线上投票
  • 移动端网站建设的请示东莞科技网站建设
  • EtherCAT转CCLKIE工业通讯网关突破:三菱PLC实时调度EtherCAT伺服完成精密加工
  • 深度学习实验一之图像特征提取和深度学习训练数据标注
  • 基于Matlab的深度堆叠自编码器(SAE)实现与分类应用
  • @Scope失效问题
  • Service 网络原理
  • 数据复制问题及其解决方案