Linux环境变量

目录

  • [1. 基本概念](#1. 基本概念)
  • [2. 常见环境变量](#2. 常见环境变量)
    • [2.1 环境变量是从哪里来的](#2.1 环境变量是从哪里来的)
  • [3. 环境变量的作用](#3. 环境变量的作用)
    • [3.1 自己的程序直接运行报错](#3.1 自己的程序直接运行报错)
    • [3.2 PATH 环境变量](#3.2 PATH 环境变量)
    • [3.3 解决报错](#3.3 解决报错)
  • [4. 环境变量相关的命令](#4. 环境变量相关的命令)
    • [4.1 echo:显示某个环境变量的值](#4.1 echo:显示某个环境变量的值)
    • [4.2 export:设置一个新的环境变量](#4.2 export:设置一个新的环境变量)
    • [4.3 env:显示所有环境变量](#4.3 env:显示所有环境变量)
    • [4.4 unset:清除环境变量](#4.4 unset:清除环境变量)
    • [4.5 set:显示本地定义的 shell 变量和环境变量](#4.5 set:显示本地定义的 shell 变量和环境变量)
  • [5. 环境变量的调用获取](#5. 环境变量的调用获取)
    • [5.1 环境变量的内存结构](#5.1 环境变量的内存结构)
    • [5.2 程序获取环境变量](#5.2 程序获取环境变量)
      • [5.2.1 main 函数的第三个参数](#5.2.1 main 函数的第三个参数)
      • [5.2.2 全局变量 environ](#5.2.2 全局变量 environ)
      • [5.2.3 getenv()](#5.2.3 getenv())
  • [6. 环境变量的全局性](#6. 环境变量的全局性)
    • [6.1 模拟进程间的环境变量传递](#6.1 模拟进程间的环境变量传递)

1. 基本概念

  1. 定义 :环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数。

    • 通俗理解:你可以把它想象成操作系统的"全局设置"或"公共便签"。系统里的各种程序在运行的时候,都会去查看这些便签,以获取自己需要的信息(比如去哪里找文件、用什么语言显示等)。
  2. 示例:

    • 我们在编写 C/C++ 代码并链接时,往往不知道链接的动态库/静态库具体在哪个文件夹,但依然能链接成功。

    • 原因 :这是因为系统里有相关的环境变量(比如 LIBRARY_PATH 等)。编译器会读取这些环境变量,环境变量告诉编译器:"去这些指定的文件夹里找库文件"。这就好比你把书放在了家里的某个柜子里,你不需要每次找书都记住具体位置,只需要看墙上的"物品存放指南"(环境变量)就能找到。

    • 以Windows为例:

  3. 特点:

    • 特殊用途:每个环境变量都有其特定的使命。

    • 全局特性:环境变量通常对当前系统或当前用户下的所有程序都生效(具有全局性),而不是只针对某一个单独的程序。

2. 常见环境变量

  1. PATH :指定命令的搜索路径。

    • 作用 :当你在终端输入一个命令(比如 ls、python)时,系统并不知道这个程序存放在硬盘的哪个角落。系统会去 PATH 变量所包含的那些目录里挨个寻找。找到了就执行,找不到就会报错 command not found。
  2. HOME :指定用户的主工作目录。

    • 作用 :也就是用户登录到 Linux 系统时,默认进入的那个目录(通常形如 /home/用户名)。当你输入 cd ~ 或者很多程序需要保存用户配置文件时,都会用到这个变量。
  3. SHELL :当前 Shell。

    • 作用 :它指定了你当前正在使用的命令行解释器是什么。在绝大多数 Linux 系统中,它的值通常是 /bin/bash。不同的 Shell(如 sh, zsh, csh)功能和语法略有不同,程序可以通过读取这个变量来知道自己处于什么命令环境下。

2.1 环境变量是从哪里来的

  • 源头一:配置文件

    当我们打开终端时,系统会去读取特定的配置文件 (比如 /etc/profile、~/.bashrc 等)。这些配置文件就像是系统的"备忘录",系统启动时会执行里面的脚本,从而生成我们的环境变量。

  • 源头二:父进程的传递

    当你打开终端,此时运行的是一个 bash 进程。**bash 进程的环境变量,**进程之间是有"父子关系"的。当前 bash 的环境变量,是由它的父进程(比如系统启动时的登录管理器)传递过来的;而父进程又是从更上级继承的。

3. 环境变量的作用

3.1 自己的程序直接运行报错

  1. 创建了一个程序 :用户通过 ls -l 查看当前目录,创建有一个名为 mycommand 的文件(绿色的字,且有 x 执行权限 -rwxr-xr-x)。手写笔记指出,这是一个用户自己实现的命令(执行后输出"我是一个命令,自己实现的!")。

  2. 成功执行(带路径) :用户输入 ./mycommand,程序成功运行了。

    • ./ 就是相对路径,本质告诉操作系统(OS),用户要执行的 bin(可执行文件),就在当前路径 下。
  3. 报错(不带路径) :用户接着直接输入 mycommand,系统报错:-bash: /usr/bin/mycommand: No such file or directory,最终提示 command not found。

    核心疑问提出 :"Linux 它自己怎么知道我应该去哪一个路径下找可执行程序啊?"

3.2 PATH 环境变量

Linux系统,会存在所谓的 环境变量的东西,PATH。

查看 PATH 的值 :用户输入了命令 echo $PATH。

这条命令的意思是------"告诉Linux系统,如果用户执行一个可执行文件,没有指明路径(比如直接敲 mycommand)" ,系统就按照 PATH 后面列出的这些路径,挨个去里面找。

PATH 里的内容 :输出结果是一串用冒号 : 隔开的目录列表(如 /usr/local/bin, /usr/bin, /home/whb/bin 等)。

为什么报错 :因为用户的 mycommand 程序存放在他当前的文件夹,而这个当前目录并没有被写进 PATH 这个环境变量里面 。所以系统去 /usr/bin 等地方找了一圈没找到,只能报错 command not found。

3.3 解决报错

临时添加当前路径到 PATH 环境变量

复制代码
export PATH=$PATH:.

这次它成功运行了! 因为系统现在会在当前目录下找命令了。

因为刚才用了 export 是临时修改,如果你关掉这个终端窗口,或者重启系统,环境变量就会恢复原样,你的 mycommand 又得加 ./ 才能运行了。要永久生效,需要把 export PATH=$PATH:. 写进 ~/.bashrc 文件里,但是不建议这么做。

4. 环境变量相关的命令

4.1. echo:显示某个环境变量的值

  • 作用 :读取并打印指定变量的内容。在 Linux 中,要获取变量的值,需要在变量名前面加上 $ 符号。如果只是敲变量名,系统会把它当成普通字符串输出。

  • 命令格式:

    复制代码
    echo $变量名
  • 示例:

复制代码
# 查看 PATH 环境变量的具体内容
$ echo $PATH
查看当前使用的 Shell 类型
$ echo $SHELL
查看当前登录的用户名
$ echo $USER

4.2. export:设置一个新的环境变量

  • 作用 :用于定义一个新的环境变量,或者修改已有环境变量的值。非常重要的一点 :使用 export 设置的变量,不仅在当前 Shell 中有效,还会传递给由当前 Shell 启动的子进程(使其具有全局属性)。

  • 命令格式:

    复制代码
    export 变量名=变量值
  • 示例:

4.3. env:显示所有环境变量

  • 作用 :env 是 environment 的缩写。它用于列出系统中当前所有的环境变量及其值。通常这个命令输出的内容会非常多,我们经常配合 grep 命令来查找特定的变量。

  • 命令格式:

    复制代码
    env 或 env | grep 关键字
  • 示例:

4.4. unset:清除环境变量

  • 作用 :删除指定的变量(包括环境变量和普通变量)。清除后,再次用 echo 查看该变量,将输出空白。

  • 命令格式:

    复制代码
    unset 变量名
  • 示例:

4.5 set:显示本地定义的 shell 变量和环境变量

  • 作用 :这是一个比较全能的命令。env 只显示环境变量,而 set 会显示所有的变量 ,包括环境变量、用户自定义的普通变量(局部变量)以及 Shell 内置的函数。

    (如果不加 export 直接定义变量,如 A=1,它只是一个本地变量,不会传递给子进程。用 set 能看到它,但用 env 看不到。)

  • 命令格式:

    复制代码
    set 或 set | grep 关键字
  • 示例:

5. 环境变量的调用获取

5.1 环境变量的内存结构

环境变量本质上就是一个字符串数组。系统把所有的环境变量打包成这种格式,存在内存里。

环境变量怎么传给我们的程序的:

本质是把环境变量表,传递给进程 ,我的程序,命令等,都是bash进程的子进程。

解释 :你在终端敲下 ./mycommand,其实是你当前所在的 bash 进程用 fork() 函数生成子进程,这个子进程就是你的 mycommand 程序。在生儿子的过程中,bash 把自己内存里的"环境变量表"原封不动地复制(传递)给了子进程。

5.2 程序获取环境变量

5.2.1 main 函数的第三个参数

复制代码
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
// env 数组以 NULL 结尾,利用这个特性遍历打印
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}

main 函数是可以带参数的!除了 argc(参数个数)和 argv(参数数组),第三个参数 env 就是环境变量表 。它是一个字符串数组,里面存满了 KEY=VALUE 这样的字符串,最后以 NULL 结束。

5.2.2 全局变量 environ

复制代码
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ; // 重点在这里!
int i = 0;
for(; environ[i]; i++){
printf("%s\n", environ[i]);
}
return 0;
}
  • libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用 extern 声明。

  • 意思是,C 标准库(libc)偷偷在内存里维护了一个指针,叫 environ。只要你用 extern 声明一下,就能直接用它遍历环境变量,连 main 函数的参数都不用写。

5.2.3 getenv()

复制代码
#include <stdio.h>
#include <stdlib.h> // getenv 在这个头文件里
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}

前面两种方法都是把所有 环境变量都打印出来。但大多数时候,我们只关心某个特定的变量(比如 PATH)。这时候用 getenv("变量名") 最方便,它会返回变量的值,如果找不到就返回 NULL。

6. 环境变量的全局性

复制代码
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *env = getenv("MYENV");
if(env){
printf("%s\n", env);
}
return 0;
}
  1. 直接运行 :代码啥也不输出。说明当前系统的环境变量里根本没有 MYENV 这个东西。

  2. 导出变量 :在终端里敲 export MYENV="hello world"。

  3. 再次运行 :程序输出了 hello world!

为什么程序能读到:

  • 因为你的程序是 bash 的子进程 。你在 bash(父进程)里 export 了一个变量,这个变量就被写入了 bash 的内存环境变量表中。

  • 当你运行程序时,bash 通过 fork() 生成了子进程,并把它的环境变量表拷贝了一份给子进程。

  • 结论:环境变量可以被子进程继承,这就是它具有"全局属性"的根本原因。

6.1 模拟进程间的环境变量传递

展示 fork() 和 exec 函数在底层是如何传递环境变量的。

  • 手动构建数组 :代码开头手动模拟了系统给我们传递参数的样子。把 argv 设置成了 ls -a,把 env 设置成了只有一条 PATH=AAAA 的列表。

  • 发生 fork() :程序走到 fork() 时,系统会创建一个子进程。

    • 子进程中(id == 0) :通过 for 循环遍历 argv 和 env 数组并打印。

    • 父进程中(id > 0) :执行了 sleep(10000),也就是挂起不动。

  • 底层逻辑 :这说明当父进程派生出子进程时,子进程会复制 父进程的内存空间。你在父进程里修改了 env 数组的值,子进程里拿到的也是你修改后的值。这是 execve 替换进程映像时的核心准备工作。

相关推荐
Shadow(⊙o⊙)1 小时前
LangChain 自定义工具全解析:从 @tool 到 bind_tools 的完整调用链路
java·服务器·前端·1024程序员节
weixin_539541771 小时前
Vue 中组件生命周期及uniapp页面生命周期的详细介绍
1024程序员节
AI技术新视界1 小时前
RSI的如何改进智能体的策略
1024程序员节
紧跟先前的步伐1 小时前
Go AI Agent 实战 13|大模型流式输出 Streaming 的实现
人工智能·ai·golang·1024程序员节
初阳7851 小时前
【基于Qt5仿音乐播放器(三)】——音乐管理
1024程序员节
CRMEB系统商城1 小时前
Redis + 消息队列 + 连接池:CRMEB PRO 性能优化的三驾马车
大数据·数据库·人工智能·小程序·php·1024程序员节
怪我冷i1 小时前
Rust 语言测试的组织结构
1024程序员节
星恒随风1 小时前
Linux网络编程基础(二):彻底理解局域网通信、MAC与IP地址、数据封装、端口号、网络字节序及Socket接口
linux·网络·笔记·学习·tcp/ip·1024程序员节
wanglong37131 小时前
Keil编译后自动重命名HEX文件
单片机·1024程序员节