Linux系统之美:环境变量的概念以及基本操作

news2025/3/24 21:43:49

本节重点

  • 理解环境变量的基本概念
  • 学会在指令和代码操作上查询更改环境变量
  • 环境变量表的基本概念
  • 父子进程间环境变量的继承与隔离

一、引入

1.1 自定义命令(我们的exe)

 我们以往的Linux编程经验告诉我们,我们在对一段代码编译形成可执行文件后执行可执行文件必须声明可执行文件所在的路径,路径可以是相对路径也可以是绝对路径:

而如果我们不加上 ./ 也就是不加上可执行文件的完整路径,系统就会出现以下报错:

表示命令行解释器(bash)无法找到该命令的可执行文件和该命令的实现方法。

1.2系统命令的实现:

系统命令的实现与上述别无二致,其主要步骤如下:

a、开发者使用编程语言(如C、C++、Shell等)编写命令的源代码。源代码包含了实现命令功能的逻辑和算法。

b、使用编译器(如gcc)将源代码编译成目标文件(.o文件),然后将目标文件链接成可执行文件。在链接过程中,编译器会链接所需的库文件,确保命令能够正常运行。

c、将编译生成的可执行文件安装到系统的某个目录中,通常是/usr/bin/sbin/usr/sbin、/等标准目录中。

所以系统命令本质上也是可执行文件,当我们在命令行输入系统命令如 ls 、cd、touch、mkdir时,bash(命令行解释器)会将命令进行解析检查命令是否合法,当合法时bash就会创建子进程来执行该命令。

对于系统命令来说,bash(命令行解释器)会自动在/usr/bin/sbin/usr/sbin、/bin等标准目录中查找相应命令的可执行文件(也就是实现方法),找到之后执行可执行文件即可。

而对于我们“自定义的命令”也就是我们的可执行文件来讲,当我们输入可执行文件的文件名时bash也会自动在/usr/bin/sbin/usr/sbin、/bin等标准目录中查找对应的可执行文件,结果当然是找不到所以就会报错(Command not found)。

1.3 引入环境变量

当我们登录Linux系统时bash进程就会被创建,同时bash进程会从系统文件中配置并维护一批环境变量来指定运行环境参数,其中 PATH 就是其中之一,其指定了/usr/bin、/sbin、/usr/sbin、/bin等标准目录:

当用户在命令行或脚本中输入一个命令或程序名时,操作系统会根据PATH环境变量的值去查找该命令或程序的可执行文件,如果找到了相应的可执行文件,操作系统就会执行它;如果没有找到,操作系统会报错,提示“命令无法识别”或类似的错误信息。

二、基本概念

在Linux系统中,环境变量是一种用于存储影响进程行为和运行的动态值的机制。环境变量是由操作系统或用户设置的,可以被系统和应用程序在运行时访问和使用。它们通常用于定义系统配置、路径设置、用户偏好等。

格式:

环境变量名 = 内容

如环境变量PATH:

一个环境变量可能会有多个内容(值),不同内容(值)之间用 :隔开。 

在命令行界面我们可以使用 env 来查看所有环境变量以及内容:

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

三、获取环境变量

3.1 指令操作

1> env

功能:查看所有或特定的环境变量

#查看所有的环境变量
env
#查看特定的环境变量(如PATH)
env | grep PATH

示例:查看特定的环境变量HOME

 

2> export

功能:设置或者更改环境变量

设置一个环境变量

export 变量名=内容

示例:设置一个新环境变量value

export value=/home/yjh/linux-learning

 

 更改一个环境变量的内容(临时):

全部更改:

export 变量名=新内容

追加更改:

export $变量名=:新内容

 示例:在环境变量PATH之后再追加一段路径/home/yjh/linux-learning

export $PATH:/home/yjh/linux-learning

3> echo

功能:查看环境变量的内容

echo $变量名

 示例:查看环境变量PATH的内容

4> unset

功能:删除某一个环境变量

unset 变量名

 示例:

3.2 代码操作

除了在命令行窗口用指令获取环境变量的内容,在代码之中我们也可以通过getenv等库函数获得对应环境变量的内容,以便于我们配置和管理运行时行为。

1> 通过父进程(bash等)获取

在理解这个概念之前我们需要明白的是main函数是什么,main函数的参数究竟有几个,main函数被谁调用这几个问题,下面我们对这些问题一一进行解答:

首先main函数是我们C程序的唯一入口点,操作系统会加载可执行文件,并寻找main函数作为执行的起点。在C语言中main函数有以下两种标准形式:

  • int main( void )
  • int main( int argc , char * argv[] )

第一种是我们常见的无参形式即不需要传递参数,第二种接受命令行参数,其中argv是一个字符串数组,而argc表示该数组中的元素个数,关于命令行参数的相关知识我们后续实现自定义shell(命令行解释器)时会给大家一一讲解。

其实在C标准中,main函数还支持第三个参数即环境变量表,所以C标准允许main函数所定义的形式为:

int main(int argc,char* argv[],char* env[])
{
    return 0;
}

其中env是一个指向环境变量字符串数组的指针,每个字符串格式为 NAME=value 。不过,这个参数在C标准中是可选的,不是所有实现都支持。例如,在Windows的某些编译器中,可能不支持env 参数,或者需要通过其他方式获取环境变量。

这里我们来写一个简单的代码打印验证一下我们通过main函数获取的环境变量:

#include<stdio.h>
int main(int argc,char* argv[],char* env[])
{
    (void)argc;
    (void)argv;
    for(int i=0;env[i];i++)
    {
        //打印数组中的每一个元素
        printf("%s\n",env[i]);                                                                                                                  
    }
    printf("\n");
    return 0;
}

运行结果:

 其中这个env字符串数组还有一个名字就是环境变量表。前面我们说过当我们登录Linux系统时系统就会给我们自动创建一个bash进程,之后我们进行的一系列命令,写的一系列代码运行起来都是bash的子进程,当我们的C程序进程被创建时bash就会调用一系列系统调用将环境变量传递给子进程形成一张环境变量表,子进程通过char* env[ ] 参数或全局变量 extern char** environ访问这些环境变量。

举个例子,当我们在bash中对环境变量进行一系列增删查改时,结果也可以在子进程(我们的C程序)中看到:

设置一组新环境变量:

export value1=123456
export value2=yjh
export value3=jzq
export value4=hello

此时再打印我们C程序中的环境变量表内容发现也可以看到新增环境变量:

2>  getenv

getenv 是一个在C标准库(<stdlib.h>)中定义的用于获取环境变量值的函数

函数原型:

char *getenv(const char *name);

功能:接受一个字符串name表示要查询的环境变量的名称,成功返回 char* 类型指向该环境变量的指针(字符串),失败返回NULL(变量不存在或不可访问)。

底层原理:全局变量environ(extern char** environ)

上面我们说过,当我们的C程序的进程被创建时,bash就会通过调用系统调用将环境变量传递给子进程(我们的C程序),形成一张环境变量表而子进程可以通过参数env或者environ来接受,其中environ是一个指向环境变量表(字符串数组)的指针。

而getenv函数内部通过遍历environ查找匹配的变量名,并返回对应环境变量的内容。

示例:查询环境变量PATH的内容

#include<stdio.h>
#include<stdlib.h>

int main()
{
    char* value=getenv("PATH");
    if(value==NULL)
    {
        printf("getenv fail\n");
    }
    else
    {
        printf("PATH->%s\n",value);
    }
    return 0;
}

运行结果:

3> environ指针

在Linux系统中,environ是一个全局变量,指向环境变量字符串数组的指针。它允许程序直接访问系统环境变量(如PATH、HOME等)。

类型extern char** environ(C语言中声明为全局变量)

内容:每个字符串格式为 name=value 例如:PATH=/usr/local/sbin

用途:程序可通过 environ 直接读取或修改环境变量

示例:

使用environ打印所有的环境变量:

#include<stdio.h>                                                                                                                                        
#include<stdlib.h>
#include<unistd.h>
extern char** environ;
int main()
{
    for(char **env=environ;*env!=NULL;env++)
    {
        printf("%s\n",*env);
    }
    return 0;
}

运行结果:

四、环境变量表

4.1 基本概念

在Linux系统中环境变量表实际上指的是存储环境变量的数据结构其本质上是一个字符串数组,它在程序启动时从父进程中被继承并在程序运行过程中可以被访问和修改。

4.2 环境变量表的产生

4.2.1 子进程中拷贝自父进程

在子进程中的环境变量表由父进程传递而来,也就是说当子进程被创建时父进程会拷贝一份自己的环境变量表传递给子进程,这里我们可以做一个小示例:

当我们在父进程(这里是bash)中对环境变量表进行一系列增删查改后,此时我们运行自己的C程序(创建bash的子进程)会发现子进程中的环境变量表也发生了改变。

在bash(父进程)设置一组新环境变量:

export value1=123456
export value2=yjh
export value3=jzq
export value4=hello

 此时打印我们C程序(子进程)中的环境变量表内容发现也可以看到新增环境变量:

注意:子进程中修改环境变量表不会影响父进程

也就是说父子进程中的环境变量表都是独立的副本,子进程被创建时父进程会拷贝自己的环境变量表加载到子进程,而子进程对自己环境变量表的修改不会影响到父进程,只会影响当前的子进程及其后续创建的子进程

4.2.2 父进程(bash)中来自系统配置

在前面我们了解到,子进程中的环境变量表由父进程中拷贝而来,而父进程(bash)中的环境变量表从何而来呢?答案是从系统配置文件而来。

在前面我们学习了一系列指令用于在命令行窗口中设置或更改环境变量,例如export和unset指令,但实际上这些操作都具有临时性,当我们退出系统并重新登录时,就会发现我们修改的结果并没有永久生效,系统会重新生成一份全新的默认的环境变量表:

在bash中新增一组环境变量:

export value1=123456
export value2=yjh
export value3=jzq
export value4=hello

此时我们可以看到新增后的结果:

当我们退出系统重新登录Linux后执行env会发现新增的一组环境变量不见了:

 bash中的环境变量来源于系统级或者用户级配置文件~/.bashrc~/.bash_profile等,export或unset指令并没有对系统配置文件进行修改所以其更改具有临时性,当我们再次登录时系统就会根据配置文件生成全新的默认的环境变量表。

五、总结

在这次学习中我们首先通过自定义可执行文件与系统指令运行时的差异上引出了环境变量的概念,之后我们了解了PATH环境变量的作用:命令行解释器(shell)通过PATH的内容遍历标准目录来查找相关指令的可执行文件,找到就运行文件找不到就报Command not found 等类似错误。

之后我们分别在指令操作与代码操作两个方面总结了查询或更改环境变量的指令与函数,在这里我们提到了环境变量的”继承性“与差异,即当子进程创建时父进程就会拷贝一份自己的环境变量表加载到子进程,而子进程对自己环境变量表的更改不会影响到父进程。

最后我们谈到了环境变量表的由来:即子进程中由父进程拷贝并加载而来,父进程(bash)中则来源于系统的配置文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2320337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pnpm 报错 Error: Cannot find matching keyid 解决

1. 查看corepack版本&#xff0c;升级至0.31.0 npm i -g corepack0.31.0 这里注意环境变量&#xff0c;可能升级后还是指向旧版本&#xff0c;可以选择更新环境变量或者删除原指向的corepack命令 2. 更新pnpm corepack install -g pnpmlatest 问题解决。

Ubuntu实时读取音乐软件的音频流

文章目录 一. 前言二. 开发环境三. 具体操作四. 实际效果 一. 前言 起因是这样的&#xff0c;我需要在Ubuntu中&#xff0c;实时读取正在播放音乐的音频流&#xff0c;然后对音频进行相关的处理。本来打算使用的PipewireHelvum的方式实现&#xff0c;好处是可以直接利用Helvum…

Fiddler抓包工具最快入门

目录 前言 了解HTTP网络知识 简单了解网络访问过程 简单了解HTTP网络传输协议 工作过程 HTTP请求&#xff1a; Fildder工具使用教程 抓包的概念 一、什么是抓包 二、为什么要抓包 三、抓包的原理&#xff08;图解&#xff09; Fiddler工具 安装 使用 Fiddler查看…

编译器与中间表示:LLVM与GCC、G++、Clang的关系详解

编译器与中间表示&#xff1a;LLVM与GCC、G、Clang的关系详解 引言 编译器是软件开发中不可或缺的工具&#xff0c;它负责将高级语言&#xff08;如C/C、Java等&#xff09;转换为机器语言&#xff0c;使计算机能够理解和执行程序。中间表示&#xff08;Intermediate Represe…

股指期货贴水波动,影响哪些投资策略?

先来说说“贴水”。简单来说&#xff0c;贴水就是股指期货的价格比现货价格低。比如&#xff0c;沪深300指数现在是4000点&#xff0c;但股指期货合约的价格只有3950点&#xff0c;这就叫贴水。贴水的大小会影响很多投资策略的收益&#xff0c;接下来我们就来看看具体的影响。 …

RHCE 使用nginx搭建网站

一。准备工作 Windows dns映射 创建目录网页 vim 编辑内容 添加如下 重启nginx服务&#xff0c;在Windows浏览器进行测试

AtCoder Beginner Contest 398(ABCDEF)

A - Doors in the Center 翻译&#xff1a; 找到一个满足下面情况长为N的字符串&#xff1a; 每个字符是 - 或 。是一个回文。包含一个或两个 。如果包含两个相邻的 。 如此字符串为独一无二的。 思路&#xff1a; 从两端使用 开始构造回文。在特判下中间部分&#xff0c;…

单表达式倒计时工具:datetime的极度优雅(智普清言)

一个简单表达式&#xff0c;也可以优雅自成工具。 笔记模板由python脚本于2025-03-22 20:25:49创建&#xff0c;本篇笔记适合任意喜欢学习的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 Pyth…

C++继承机制:从基础到避坑详细解说

目录 1.继承的概念及定义 1.1继承的概念 1.2 继承定义 1.2.1定义格式 1.2.2继承关系和访问限定符 1.2.3继承基类成员访问方式的变化 总结&#xff1a; 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 ​编辑 默认构造与传参构造 拷贝构造&am…

MySQL数据库精研之旅第二期:库操作的深度探索

专栏&#xff1a;MySQL数据库成长记 个人主页&#xff1a;手握风云 目录 一、查看数据库 二、创建数据库 2.1. 语法 2.2. 示例 三、字符集编码和校验(排序)规则 3.1. 查看数据库支持的字符集编码 3.2. 查看数据库支持的排序规则 3.3. 不同的字串集与排序规则对数据库的…

git_version_control_proper_practice

git_version_control_proper_practice version control&#xff0c;版本控制的方法之一就是打tag 因为多人协作的项目团队&#xff0c;commit很多&#xff0c;所以需要给重要的commit打tag&#xff0c;方便checkout&#xff0c;检出这个tag 参考行业的实践方式。如图git、linux…

计算机组成原理和计算机网络常见单位分类及换算

计算机组成原理&#xff08;主要用于存储、内存、缓存等&#xff09; 计算机网络&#xff08;主要用于传输速率&#xff09; 直观对比

【第二十八周】:Temporal Segment Networks:用于视频动作识别的时间分段网络

TSN 摘要Abstract文章信息引言方法时间分段采样分段聚合输入模态聚合函数多尺度时序窗口集成&#xff08;M-TWI&#xff09;训练 代码实现实验结果总结 摘要 本篇博客介绍了时间分段网络&#xff08;Temporal Segment Network, TSN&#xff09;&#xff0c;这是一种针对视频动…

扩展域并查集

什么叫扩展域并查集 1 和 2是敌人&#xff0c;那么就把1好12链接起来&#xff1a;表示1和2是敌人 2和11链接起来也是这个道理 然后2 和3使敌人同理。 最后12连接了1 和 3&#xff0c;表名1 和 3 是 2 的敌人&#xff0c;1和3 就是朋友 1.P1892 [BalticOI 2003] 团伙 - 洛谷 #in…

【C#语言】C#同步与异步编程深度解析:让程序学会“一心多用“

文章目录 ⭐前言⭐一、同步编程&#xff1a;单线程的线性世界&#x1f31f;1、寻找合适的对象✨1) &#x1f31f;7、设计应支持变化 ⭐二、异步编程&#xff1a;多任务的协奏曲⭐三、async/await工作原理揭秘⭐四、最佳实践与性能陷阱⭐五、异步编程适用场景⭐六、性能对比实测…

动态规划入门详解

动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09;是一种算法思想&#xff0c;它将问题分解为更小的子问题&#xff0c;然后将子问题的解存起来&#xff0c;避免重复计算。 所以动态规划中每一个状态都是由上一个状态推导出来的&#xff0c;这一点就区别…

SOFABoot-09-模块隔离

前言 大家好&#xff0c;我是老马。 sofastack 其实出来很久了&#xff0c;第一次应该是在 2022 年左右开始关注&#xff0c;但是一直没有深入研究。 最近想学习一下 SOFA 对于生态的设计和思考。 sofaboot 系列 SOFABoot-00-sofaboot 概览 SOFABoot-01-蚂蚁金服开源的 s…

基于基于eFish-SBC-RK3576工控板的智慧城市边缘网关

此方案充分挖掘eFish-SBC-RK3576的硬件潜力&#xff0c;可快速复制到智慧园区、交通枢纽等场景。 方案亮点 ‌接口高密度‌&#xff1a;单板集成5GWiFi多路工业接口&#xff0c;减少扩展复杂度。‌AIoT融合‌&#xff1a;边缘端完成传感器数据聚合与AI推理&#xff0c;降低云端…

CSS基础知识一览

持续维护 选择器 display 常用属性 浮动 弹性布局

【免费】2000-2019年各省地方财政房产税数据

2000-2019年各省地方财政房产税数据 1、时间&#xff1a;2000-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政房产税 4、范围&#xff1a;31省 5、指标说明&#xff1a;房产税是对个人和单位拥有的房产征收的一种…