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

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

引言

在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命令不带路径执行,系统也是能够找到的。
那么我们有没有什么办法能够让自己的程序不带路径也能执行呢?——我们通过两种方法实现 

方式一:将可执行程序拷贝到环境变量PATH的某一路径下。

  • 既然在未指定路径的情况下系统会根据环境变量PATH当中的路径进行查找,那我们就可以将我们的可执行程序拷贝到PATH的某一路径下,此后我们的可执行程序不带路径系统也可以找到了。
sudo cp 文件名 /usr/bin
在这里插入图片描述


方式二:将可执行程序所在的目录导入到环境变量PATH当中。

  • 将可执行程序所在的目录导入到环境变量PATH当中,这样一来,没有指定路径时系统就会来到该目录下进行查找了。
  • $PATH:添加我们的路径,$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++ 程序的入口点,它通过命令行参数机制为程序提供了与外部环境交互的重要接口。其标准形式为:

intmain(int argc,char* argv[])

参数解析:

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

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

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

./mycmd -a file.txt 

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

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

指令原型:

#include<stdio.h>#include<string.h>intmain(int argc,char* argv[]){// 检查参数数量:程序需要恰好1个参数(不含程序名本身)if(argc !=2){// 显示正确的使用格式提示printf("用法: %s -[a|b|c]\n", argv[0]);return1;// 返回非零值表示执行失败}// 输出程序名和用户输入的参数,便于调试和显示printf("%s[1]->%s ", argv[0], argv[1]);// 根据不同的命令行选项执行相应功能if(strcmp(argv[1],"-a")==0){// 处理 -a 选项:执行功能1printf("功能1\n");}elseif(strcmp(argv[1],"-b")==0){// 处理 -b 选项:执行功能2printf("功能2\n");}elseif(strcmp(argv[1],"-c")==0){// 处理 -c 选项:执行功能3printf("功能3\n");}else{// 处理未知选项:提示用户输入错误printf("未知选项\n");return1;// 返回非零值表示执行失败}// 程序正常执行完成return0;}
在这里插入图片描述

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 参数,代码更加简洁。
#include<stdio.h>#include<string.h>#include<stdlib.h>intmain(int argc,char* argv[]){int cnt =0;while(argv[cnt]!=NULL){printf("%s[%d]->%s\n", argv[0], cnt, argv[cnt]); cnt++;}return0;}
在这里插入图片描述

7.3 命令行第三个参数

char* env[]main 函数的第三个参数,用于接收环境变量表
  • 环境变量指针数组env 是一个以 NULL 结尾的字符串指针数组
  • 存储内容:每个指针指向一个 “KEY=VALUE” 形式的环境变量字符串
  • 系统传递:由操作系统在程序启动时自动填充并传递给程序
  • 遍历方式:与 argv 类似,可以通过循环遍历直到遇到 NULL 指针

打印出环境变量表:

#include<stdio.h>intmain(int argc,char*argv[],char*env[]){int i =0;for(; env[i]; i++){printf("%s\n", env[i]);}return0;}
在这里插入图片描述

7.4 通过第三方变量environ获取

#include<stdio.h>intmain(int argc,char*argv[]){externchar**environ;int i =0;for(; environ[i]; i++){printf("%s\n", environ[i]);} r eturn 0;}
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时要用extern声明。 
在这里插入图片描述

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

8.1 getenv

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

#include<stdio.h>#include<stdlib.h>intmain(){printf("PATH:%s\n",getenv("USER"));printf("USER:%s\n",getenv("USER"));return0;}
在这里插入图片描述

九、环境变量的全局属性

  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

我们运行的程序,也是fork的子进程,继承fork的环境变量,那么我们使用代码通过getenv测试我们定义的本地变量MYENV是否被继承下来

在这里插入图片描述

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

在这里插入图片描述

我们还可以使用set去查看这个MYENV本地变量

在这里插入图片描述

我们再使用echo $查看MYNV,查看到MYENV对应的值了555

在这里插入图片描述
结果:运行程序,访问出现错误,子进程中的环境变量中并没有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状态且效率更高。


✨ 坚持用清晰易懂的图解+代码语言, 让每个知识点都简单直观!
🚀 个人主页不呆头 · ZEEKLOG
🌱 代码仓库不呆头 · Gitee
📌 专栏系列 :📖 《C语言》🧩 《数据结构》💡 《C++》🐧 《Linux》💬 座右铭 :“不患无位,患所以立。”

Read more

在 VSCode 中本地运行 DeepSeek,打造强大的私人 AI

在 VSCode 中本地运行 DeepSeek,打造强大的私人 AI

本文将分步向您展示如何在本地安装和运行 DeepSeek、使用 CodeGPT 对其进行配置以及开始利用 AI 来增强您的软件开发工作流程,所有这些都无需依赖基于云的服务。  步骤 1:在 VSCode 中安装 Ollama 和 CodeGPT         要在本地运行 DeepSeek,我们首先需要安装Ollama,它允许我们在我们的机器上运行 LLM,以及CodeGPT,它是集成这些模型以提供编码辅助的 VSCode 扩展。 安装 Ollama Ollama 是一个轻量级平台,可以轻松运行本地 LLM。 下载Ollama 访问官方网站:https://ollama.com * 下载适合您的操作系统(Windows、macOS 或 Linux)的安装程序。 * 验证安装 安装后,打开终端并运行: ollama --version  如果 Ollama 安装正确,

By Ne0inhk
DeepSeek-R1是真码农福音?我们问了100位开发者……

DeepSeek-R1是真码农福音?我们问了100位开发者……

从GitHub Copilot到DeepSeek-R1,AI编程工具正在引发一场"效率革命",开发者们对这些工具的期待与质疑并存。据Gartner预测,到2028年,将有75%的企业软件工程师使用AI代码助手。 眼看着今年国产选手DeepSeek-R1凭借“深度思考”能力杀入战场,它究竟是真码农福音还是需要打补丁的"潜力股"? ZEEKLOG问卷调研了社区内来自全栈开发、算法工程师、数据工程师、前端、后端等多个技术方向的100位开发者(截止到2月25日),聚焦DeepSeek-R1的代码生成效果、编写效率、语法支持、IDE集成、复杂代码处理等多个维度,一探DeepSeek-R1的开发提效能力。 代码生成效果:有成效但仍需提升 * 代码匹配比例差强人意 在代码生成与实际需求的匹配方面,大部分开发者(58人)遇到生成代码与实际需求完全匹配无需修改的比例在40%-70%区间,12人遇到代码匹配比例在70%-100%这样较高的区间。 然而,有30人代码匹配比例低于40%。这说明DeepSeek-R1在代码生成方面有一定效果,但在部分复杂或特定场景下,仍有很大的提升空间。

By Ne0inhk
AI+游戏开发:如何用 DeepSeek 打造高性能贪吃蛇游戏

AI+游戏开发:如何用 DeepSeek 打造高性能贪吃蛇游戏

文章目录 * 一、技术选型与准备 * 1.1 传统开发 vs AI生成 * 1.2 环境搭建与工具选择 * 1.3 DeepSeek API 初步体验 * 二、贪吃蛇游戏基础实现 * 2.1 游戏结构设计 * 2.2 初始化游戏 * 2.3 DeepSeek 生成核心逻辑 * 三、游戏功能扩展 * 3.1 多人联机模式 * 3.2 游戏难度动态调整 * 3.3 游戏本地保存与回放 * 3.4 跨平台移植 * 《Vue.js项目开发全程实录/软件项目开发全程实录》 * 编辑推荐 * 内容简介 * 作者简介 * 目录 一、

By Ne0inhk
[DeepSeek] 入门详细指南(上)

[DeepSeek] 入门详细指南(上)

前言 今天的是 zty 写DeepSeek的第1篇文章,这个系列我也不知道能更多久,大约是一周一更吧,然后跟C++的知识详解换着更。 来冲个100赞兄弟们 最近啊,浙江出现了一匹AI界的黑马——DeepSeek。这个名字可能对很多人来说还比较陌生,但它已经在全球范围内引发了巨大的关注,甚至让一些科技巨头感到了压力。简单来说这 DeepSeek足以改变世界格局                                                   先   赞   后   看    养   成   习   惯  众所周知,一篇文章需要一个头图                                                   先   赞   后   看    养   成   习   惯   上面那行字怎么读呢,让大家来跟我一起读一遍吧,先~赞~后~看~养~成~习~惯~ 想要 DeepSeek从入门到精通.pdf 文件的加这个企鹅群:953793685(

By Ne0inhk