深入理解环境变量

news2024/11/18 9:31:25

前言

指令就是可执行程序,当我们运行自己的可执行程序时,需要用./来指定路径,可是为什么运行指令时不用指定路径呢?这就是环境变量的作用。

一.常见环境变量

环境变量是在程序运行期间需要用到的具有特定功能的一组变量,这些变量从配置文件中获取,存放于shell内部。说白了就是内存里的一些变量,里面存放了一堆字符串。

PATH

执行一个指令的前提是找到对应的可执行程序,执行一个指令时不需要带路径,就是因为系统会首先在默认搜索路径下查找,而这个默认搜索路径就在PATH这个环境变量中存着。要想查看一个环境变量,只需在变量名前加上$符号,然后echo(输出到屏幕)

可以看到,PATH里的内容是一个字符串,里面有很多路径,用冒号分隔,而我们的大多数指令就存放在其中的/usr/bin目录下。

所以要想自己的程序不带路径就能运行,有两种方法,第一是将程序拷贝到PATH中的某个路径下,例如/usr/bin;第二种方法是把可执行程序所在的路径添加到环境变量中去。

如图这就是修改环境变量的方法,直接给它赋值。由于我们是想添加内容,所以要$PATH后面加上我们想要添加的路径,然后要注意分隔符冒号。

PWD

PWD这个环境变量里存放的是当前所在路径,当我们使用pwd指令时,实际上就是读取了PWD的值。

HOME 

每个用户都有自己的家目录,普通用户的家目录是/home/xxx,root的家目录是/root,家目录存放在存放在HOME这个环境变量中

为什么普通用户登录成功后,默认处于家目录/home/xxx,root登录成功后就处于/root呢? 

登录的时候:

1.输入用户名和密码

2.认证

3.形成环境变量(不止一个,如PATH,PWD,HOME等)

        根据用户名,初始化HOME=/root 或 /home/xxx

4.cd $HOME

二.查看环境变量

env

注意此时的USER

su  

 

可以发现USER没有变化,并且当前所处的目录也不在root的家目录下 ,但是家目录确实已经变成/root了。

su -

 可以发现,su-后USER变成root了,并且也处在家目录下。

总结:su只是把用户身份切换成root,而su -是让系统以root身份重新登录

三.在代码中调用函数获取环境变量

#include <stdlib.h>

char* getenv(char* name)

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

int main()    
{    
    char* who = getenv("USER");    
    if (strcmp(who, "root") == 0)                                                                                                                                                   
    {                                                                                                                                                  
        printf("my command\n");                                                                                                                        
        printf("my command\n");                                                                                                                        
        printf("my command\n");                                                                                                                        
        printf("my command\n");                                                                                                                        
    }                                                                                                                                                  
    else                                                                                                                                               
    {                                                                                                                                                  
        printf("You are not root, you don't have power!!!\n");
    }
    return 0;
}

 功能:拦截普通用户运行这个程序

四.命令行参数中获取环境变量

我们可能很少使用命令行参数,实际上执行运行程序时我们可以传递一些参数,这些参数能被main函数接收。main函数可以不带参,可以带两个参数,也可以带三个参数,第一个参数是int argc,接收参数的个数,第二个参数是char* argv[],接收参数数组,即字符指针数组。而第三个参数char* env[]接收的就是环境变量。

1.命令行参数表环境变量表

当shell登录时,系统会给shell创建一个环境变量表,也就是一个字符指针数组,每个指针指向一个环境变量。

系统启动程序的时候,可以选择给我们的进程(main函数)提供两张表:

1.命令行参数表:

2.环境变量表

  #include <stdio.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  
  int main(int argc, char* argv[], char* env[])    
  {    
      for (int i = 0; env[i]; i++)    
      {    
          printf("pid: %d, env[%d]: %s\n", getpid(), i, env[i]);    
      }    
      return 0;    
  }    

打印出来的信息和用使用env打印出来的信息是一模一样的。

2.命令行参数环境变量是由谁传递的

我们在命令行中启动的进程,实质上是shell(bash)的子进程,子进程的命令行参数和环境变量是父进程bash给我们传递的。

bash创建子进程,会获取我们输入的命令行参数,对它进行处理,然后传给子进程,这个好理解。但是,父进程的环境变量又从哪里来的呢?

我们将PATH清空,发现ls,env等命令就无法执行了,但是重新登录,发现指令又能执行了。

实际上,我们更改的是bash进程内部的环境变量信息。每一次登录,都会给我们形成新的bash进程,bash进程自动从某个地方读取信息形成自己的环境变量表信息。这个地方就是脚本配置文件,家目录下的.bash_profile文件。

发现其中还嵌套了.bashrc文件

继续套娃,/etc/bashrc 

这里面的内容可就多了,有100行的内容,各种各样的环境变量信息。

五.手动添加环境变量

我们也可以手动向环境变量表中导入变量

添加一个变量MYENV,赋值为hello,再用env查看环境变量,发现MYENV并不存在。实际上我们创建的ENV叫做本地变量,并没有导入到环境变量表当中。要想将本地变量变成环境变量,只需export一下:

也可以在在定义变量时就导出到环境变量列表:

但是,当我们重新登录时,刚才自定义环境变量也就没有了。因为不管是本地变量还是环境变量,都是保存在bash进程中,是内存中的数据,bash退出,也就什么都没有了。所以如果我们想要定义一个环境变量,并且让它永久存在,就要更改配置文件。

当我们再登录时,新的bash就会读取脚本文件,形成环境变量。

六.环境变量的全局属性

bash的子进程会通过命令行参数的方式接收bash的环境变量,实际上,普通进程不用命令行参数的方式,也有办法将环境变量传下去。所以说,环境变量具有全局属性,可以父传子,子传孙,一直继承下去。

七.使用全局变量获取环境变量

前面说普通进程fork出来的进程也能继承父进程的环境变量,这是怎么做到的呢?

C语言库为进程提供了一个全局指针变量,char** environ,它指向环境变量表,这个变量定义在头文件<unistd>中。

不管是命令行参数还是环境变量,父进程bash都要先拿到,命令行参数和环境变量都是bash的数据。而bash创建进程也是通过fork创建的,而fork创建子进程,父子进程代码共享,数据以写时拷贝的方式各自私有一份,所以environ这个指针变量,每个进程都能访问到的,有了这个指针,也就能找到那张环境变量表了。

#include <stdio.h>
#include <unistd.h>

int main()    
{    
    extern char** environ;//声明全局变量    
    for (int i = 0; environ[i]; i++)    
    {    
        printf("environ[%d]: %s\n", i, environ[i]);                                                                                                                                 
    }    
    return 0;    
}    

八.小结:获取环境变量的四种方式

1.env指令

环境变量列表是bash自己通过读取脚本配置文件创建的,所以在命令行,也就是bash内部,自然能够访问到环境变量列表 

2.char* env[]接收命令行参数

bash可以以指针方式给子进程传递环境变量列表,main函数用变量env接收命令行参数,所以能够通过这个变量访问到环境变量列表

3.使用environ全局变量

C语言库提供了一个指向环境变量列表的全局指针变量environ,这个变量会被子进程继承下去,所以每个进程都会直接或则间接地继承environ这个全局变量,从而能够找到bash中的环境变量列表,访问环境变量

4.使用getenv函数

此函数用于访问特定的环境变量,参数传入你要获取的环境变量名字。

九. 本地变量和环境变量的区别

本地变量:本地变量只在bash内部有效,不会被子进程继承下去(不在环境变量表中)

环境变量:环境变量通过让所有子进程继承的方式,实现自身的全局属性

十.常规命令与内建命令

这里有一个问题,在bash中定义了一个本地变量a,前面说本地变量不具有全局属性,只在bash内部有效,子进程访问不到。但是echo是一条指令,它也是一个子进程呀,它怎么就能访问到a呢?

再看一个场景:

我们将PATH清空,ls,env这些指令就不能直接跑了,但是为什么echo指令不带路径还能执行呢?

这是因为echo和我们平常使用的命令不一样!!!

Linux的命令分为两类:

1.常规命令:磁盘上存在的可执行程序,shell(bash) fork创建子进程,让子进程执行任务

2.内建命令:shell(bash)的一个函数,不创建子进程,而是调用函数来执行任务

当bash识别到要执行echo命令,它会去调用自己内部的一个函数,这个函数名字就叫echo。由于这个这个函数是shell内部的,当然可以读取shell内部定义的本地变量喽,并且它也不需要什么默认搜索路径,直接调用内部的函数就好啦。

同样,export指令也是一个内建命令,因为如果让子进程去导入环境变量,那么环境变量不就到子进程的内存空间里去了。

当然,系统中的大多数指令都是常规命令,只有一些安全,短小的命令shell才会亲自执行。

十一.和环境变量有关的指令

1. echo: 显示某个变量值(本地变量和环境变量)

2. export: 导出一个新的环境变量

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

4. unset: 清除某个环境变量

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

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

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

相关文章

来看看如何使用CLOUDFARE_实现网络聚合_利用安全的网络协议实现网络通讯---工具箱工作笔记002

下面这个操作是需要进行安全网络下操作的 首先需要去访问https://1.1.1.1这样一个网站然后去下载对应的软件,安装上 首先进入这个网址 www.cloudflare.com 去点击注册 注册以后 然后登录,登录以后,然后左侧有个zero trust 然后再去写一个名字,团队的名字 然后选择也是免…

win11快速打开蓝牙设置的方法

win11快速打开蓝牙设置的方法 Windows 11 中快速连接蓝牙设备的 3 种方法&#xff01;_哔哩哔哩_bilibili 如何为Windows设置快捷键&#xff1f;_百度知道 (baidu.com) Win11怎么隐藏文件夹?Win11通过命令隐藏文件夹的方法_windows11_Windows系列_操作系统_脚本之家 (jb51.net…

2020年江西省职业院校技能大赛软件测试技能竞赛方案(高职组)

2020年江西省职业院校技能大赛 软件测试技能竞赛方案&#xff08;高职组&#xff09; 各高等职业院校&#xff1a; 根据江西省教育厅《关于举办2020年江西省职业院校技能大赛的通知》&#xff08;赣教职成字〔2020〕19号&#xff09;文件精神&#xff0c;现举办2020年江西省职业…

【软考】11.1 生命周期/CMM/开发模型

《信息系统生命周期》 软件工程的基本要素&#xff1a;方法、工具、过程 五阶段生命周期的定义和输出 规划 ——> 分析 ——> 设计 ——> 实施 ——> 运行和维护系统分析阶段&#xff1a;逻辑设计 ——> 逻辑模型系统设计阶段&#xff1a;物理设计 ——> 物…

常用的设计模式以及操作Redis、MySQL数据库、各种MQ、数据类型转换的方法

文章目录 &#x1f31f; 如何优雅地写出高质量的Java代码&#x1f34a; 设计模式&#x1f389; 单例模式&#x1f389; 工厂模式&#x1f389; 观察者模式 &#x1f34a; 操作Redis&#x1f389; 连接Redis&#x1f389; 存储数据&#x1f389; 获取数据&#x1f389; 删除数据…

SpringBoot环境搭建与初创程序

一&#xff1a;IDEA环境准备 IDEA社区版版本: 2021.1-2022.1.4 IDEA专业版版本: 无要求 &#x1f31f;如果个人电脑安装的IEDA不在这个范围&#xff0c;需要卸载重新安装&#xff1b;且⼀定要删除注册表 参考文章➜IDEA卸载和删除注册表 二&#xff1a; Maven (1)Maven的概念…

Java练习题-输出斐波那契(Fibonacci)数列

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;Java练习题 &#x1f4ac;个人格言&#xff1a;不断的翻越一座又…

CSS3属性详解(一)文本 盒模型中的 box-ssize 属性 处理兼容性问题:私有前缀 边框 背景属性 渐变 前端开发入门笔记(七)

CSS3是用于为HTML文档添加样式和布局的最新版本的层叠样式表&#xff08;Cascading Style Sheets&#xff09;。下面是一些常用的CSS3属性及其详细解释&#xff1a; border-radius&#xff1a;设置元素的边框圆角的半径。可以使用四个值设置四个不同的圆角半径&#xff0c;也可…

自动化测试08

Junit 为什么学了Selenium还需学习Junit Selenium自动化测试框架&#xff1b;Junit单元测试框架。 拿着一个技术写自动化测试用例&#xff08;Selenium3&#xff09; 拿着一个技术管理已经编写好的测试用例&#xff08;Junit5&#xff09; Junit相关的技术 Junit是针对Java的一…

Cesium Vue(五)— 绘制多边形

1. 使用entity创建矩形 var rectangle viewer.entities.add({rectangle: {coordinates: Cesium.Rectangle.fromDegrees(// 西边的经度90,// 南边维度20,// 东边经度110,// 北边维度30),material: Cesium.Color.GREEN.withAlpha(0.8),},2. 使用primivite创建矩形 // primivite创…

【公众号开发】Access Token的获取 · 请求公众号服务器创建自定义菜单 · 处理自定义菜单按钮事件

【公众号开发】&#xff08;3&#xff09; 文章目录 【公众号开发】&#xff08;3&#xff09;1. 获取Access token1.1 确定参数1.2 补全URL&#xff08;添加query string&#xff09;1.3 测试 2. 封装AccessToken以便保存与后期使用2.1 TokenUtils做出一些调整2.2 单例模式的A…

YOLOv5改进实战 | 更换主干网络Backbone(三)之轻量化模型Shufflenetv2

前言 轻量化网络设计是一种针对移动设备等资源受限环境的深度学习模型设计方法。下面是一些常见的轻量化网络设计方法: 网络剪枝:移除神经网络中冗余的连接和参数,以达到模型压缩和加速的目的。分组卷积:将卷积操作分解为若干个较小的卷积操作,并将它们分别作用于输入的不…

【手把手教你】使用Python玩转多元时间序列分析

在探索和理解复杂的金融市场行为时&#xff0c;时间序列分析成为了一种无法忽视的强有力工具。特别是&#xff0c;当我们处理的不仅是单一的时间序列&#xff0c;而是多个时间序列并存&#xff0c;并且它们之间存在一种或多种形式的互动时&#xff0c;多元时间序列分析的重要性…

线性代数3:矢量方程

一、前言 欢迎回到系列文章的第三篇文章&#xff0c;内容是线性代数的基础知识&#xff0c;线性代数是机器学习背后的基础数学。在我之前的文章中&#xff0c;我介绍了梯队矩阵形式。本文将介绍向量、跨度和线性组合&#xff0c;并将这些新想法与我们已经学到的内容联系起来。本…

如何使用内网穿透技术实现USB设备(USB Redirector)共享

文章目录 前言1. 安装下载软件1.1 内网安装使用USB Redirector1.2 下载安装cpolar内网穿透 2. 完成USB Redirector服务端和客户端映射连接3. 设置固定的公网地址 前言 USB Redirector是一款方便易用的USB设备共享服务应用程序&#xff0c;它提供了共享和访问本地或互联网上的U…

驱动开发 CoetexA7核 字符设备驱动(LED亮灯)(单独映射寄存器实现+封装结构体映射实现)

一、单独映射寄存器实现 可参考arm点灯C语言 cortex-A7核 点LED灯 &#xff08;附 汇编实现、使用C语言 循环实现、使用C语言 封装函数实现【重要、常用】&#xff09;-CSDN博客 1 应用程序 test.c #include <stdio.h> #include <sys/types.h> #include <sys/s…

我的电子萝卜刀火了吗?

引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 笔者在上一篇文章《萝卜刀真的太危险了,于是我用Cocos做了一个》中说到因女儿从学校回来之后想要我给她买一把萝卜刀被我拒绝&#xff0c;但是又想要让她体验一下&#xff0c;因此用Cocos…

【广州华锐互动】建筑安全事故VR沉浸式体验系统

在建筑行业中&#xff0c;安全永远是首要的考虑因素。传统的安全培训方法&#xff0c;如书本教学、现场演示等&#xff0c;虽然能在一定程度上提高员工的安全意识&#xff0c;但这些方法往往缺乏实际体验&#xff0c;员工在真正面临危险时可能无法做出正确的判断和反应。近年来…

nvm管理不同版本nodejs

文章目录 nvm下载卸载本地node安装nvm安装nodejsnvm查看已安装版本nvm切换nodejs版本nvm删除nodejs版本 nvm下载 nvm github下载链接 nvm 1.1.7-setup.zip&#xff1a;安装版&#xff0c;推荐使用 卸载本地node 打开cmd where node 找到上面找到的路径&#xff0c;将node.…

gulp打包vue3+jsx+less插件

最终转换结果如下 在根目录下添加gulpfile.js文件&#xff0c;package.json添加命令npm run gulp var gulp require(gulp) var babel require(gulp-babel) var less require(gulp-less) var del require(del); var spawn require(child_process).spawn;const outDir &…