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

本节重点

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

一、引入

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.rhkb.cn/news/38208.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

尝试在软考65天前开始成为软件设计师-计算机网络

OSI/RM 七层模型 层次名功能主要协议7应用层实现具体应用功能 FTP(文件传输)、HTTP、Telnet、 POP3(邮件)SMTP(邮件) ------- DHCP、TFTP(小文件)、 SNMP、 DNS(域名) 6表示层数据格式,加密,压缩.....5会话层建立,管理&终止对话4传输层端到端连接TCP,UDP3网络层分组传输&a…

基于随机森林回归预测葡萄酒质量

基于随机森林回归预测葡萄酒质量 1.作者介绍2.随机森林算法与数据集介绍2.1定义2.2核心思想2.3主要步骤2.4数据集介绍 3.算法实现3.1数据加载与探索3.2数据可视化3.3数据预处理&#xff08;标准化、划分训练/测试集&#xff09;3.4模型训练与优化&#xff08;随机森林回归 超参…

【赵渝强老师】在Docker中运行达梦数据库

Docker是一个客户端服务器&#xff08;Client-Server&#xff09;架构。Docker客户端和Docker守护进程交流&#xff0c;而Docker的守护进程是运作Docker的核心&#xff0c;起着非常重要的作用&#xff08;如构建、运行和分发Docker容器等&#xff09;。达梦官方提供了DM 8在Doc…

【C语言】深入理解指针(二):从数组到二维数组的指针魔法

前言 在C语言中&#xff0c;指针一直是一个神秘而强大的存在。它不仅可以帮助我们高效地操作内存&#xff0c;还能让代码更加灵活和高效。今天&#xff0c;我们就来深入探讨指针的多种用法&#xff0c;从数组到二维数组&#xff0c;一步步揭开指针的神秘面纱。 一、数组名的指…

【MySQL】事务

目录 基本概念事务操作自动提交事务开启事务提交事务回滚事务代码示例 事务的特性 ACID事务的隔离级别读未提交 read uncommitted读已提交 read committed可重复读 repeatable read序列化&#xff08;串行&#xff09; serializable操作示例 基本概念 在 MySQL 中的事务&#…

flutter doctor提示cmdline-tools component is missing错误的解决

flutter doctor检测环境后报错如下: STEP1: 配置command-lines &#x1f4cc; 打开Androidstudio &#xff0c;找到sdkmanager &#x1f447; 安装command-line tools 如果找不到&#xff0c;记得打开右下角的「Show Package Details} 再次运行flutter doctor 即可正常 如…

iptables和netfilter内部报文处理

一、Iptables和netfilter 1.iptables基础 netfilter强大功能以及灵活性是通过iptables界面来实现。此命令行工具和它的前身ipchains语法相似&#xff1b;不过iptables使用netfilter子系统来增进网络连接、检验和处理方面的能力&#xff1b;ipchains使用错综复杂的规则集合来过…

[项目]基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050配置与读取

基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050 一.芯片介绍二.配置I2C三.编写驱动四.读取任务的测试 一.芯片介绍 芯片应该放置在PCB中间&#xff0c;X Y轴原点&#xff0c;敏感度131表示范围越小越灵敏。理想状态放置在地面上X&#xff0c;Y&#xff0c;Z轴为0&#xff0c;即…

JVM垃圾回收笔记01

文章目录 前言1. 如何判断对象可以回收1.1 引用计数法1.2 可达性分析算法查看根对象哪些对象可以作为 GC Root ?对象可以被回收&#xff0c;就代表一定会被回收吗&#xff1f; 1.3 引用类型1.强引用&#xff08;StrongReference&#xff09;2.软引用&#xff08;SoftReference…

解决Popwindow宽高的问题。

问题 在使用Popwindow进行自定义的过程中&#xff0c;需要设置popwindow的宽高。但是宽高很多时候容易出问题。比如下面的例子。 布局文件如下 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.andr…

Bell-1量子计算机分析:开启量子计算2.0时代的创新引擎

Bell-1量子计算机:开启量子计算2.0时代的创新引擎 一、引言 1.1 研究背景 在当今科技飞速发展的时代,量子计算作为前沿领域,正深刻地改变着科技格局,引领新一轮科技革命与产业变革。自 20 世纪 80 年代量子计算概念被提出以来,历经多年的理论探索与技术攻坚,已取得了众…

什么?中断禁用失效了?

什么&#xff1f;中断禁用失效了&#xff1f; 1. 前言 道友们&#xff0c;在嵌入式的开发中我们不管是RTOS或NO-RTOS的开发&#xff0c;都无法避免“多线程”的应用场景&#xff0c;高优先级的任务或中断打断低优先级的任务或中断&#xff0c;此时为了要保证共享数据的安全性…

单表达式倒计时工具:datetime的极度优雅(Kimi)

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

[笔记.AI]多头自注意力机制(Multi-Head Attention)

多头自注意力是深度学习领域&#xff0c;特别是自然语言处理&#xff08;NLP&#xff09;和Transformer模型中的关键概念。其发展源于对序列数据中复杂依赖关系的建模需求&#xff0c;特别是在Transformer架构的背景下。 举例 比喻-读长篇文章 用一个简单的比喻来理解“多头注…

SOFABoot-02-模块化隔离方案

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

【实用部署教程】olmOCR智能PDF文本提取系统:从安装到可视化界面实现

文章目录 引言系统要求1. 环境准备&#xff1a;安装Miniconda激活环境 2. 配置pip源加速下载3. 配置学术加速&#xff08;访问国外资源&#xff09;4. 安装系统依赖5. 安装OLMOCR6. 运行OLMOCR处理PDF文档7. 理解OLMOCR输出结果9. 可视化UI界面9.1 安装界面依赖9.2 创建界面应用…

asp.net core mvc模块化开发

razor类库 新建PluginController using Microsoft.AspNetCore.Mvc;namespace RazorClassLibrary1.Controllers {public class PluginController : Controller{public IActionResult Index(){return View();}} }Views下Plugin下新建Index.cshtml {ViewBag.Title "插件页…

边缘计算革命:重构软件架构的范式与未来

摘要 边缘计算通过将算力下沉至网络边缘&#xff0c;正在颠覆传统中心化软件架构的设计逻辑。本文系统分析了边缘计算对软件架构的范式革新&#xff0c;包括分布式分层架构、实时资源调度、安全防护体系等技术变革&#xff0c;并结合工业物联网、智慧医疗等场景案例&#xff0c…

单链表:数据结构的灵动之链

本文主要讲解链表的概念和结构以及实现单链表 目录 一、链表的概念及结构 二、单链表的实现 1.1链表的实现&#xff1a; 1.2单链表的实现&#xff1a; 单链表尾插&#xff1a; 单链表的头插&#xff1a; 单链表的尾删&#xff1a; 单链表头删&#xff1a; 单链表查找&#…

链表题型-链表操作-JS

一定要注意链表现在的头节点是空节点还是有值的节点。 一、移除链表中的元素 有两种方式&#xff0c;直接使用原来的链表进行删除操作&#xff1b;设置一个虚拟头节点进行删除操作。 直接使用原来的链表进行删除操作时&#xff0c;需要考虑是不是头节点&#xff0c;因为移除…