[入门篇]Linux环境变量,万字巨作,爆炸解析!错过即损失,不妨点进来看看!

目录

0.前言

1. 引入环境变量PATH

1.1概念铺垫

1.2环境变量的查看与设置(以PATH为例)

1.3 修改环境变量(以PTAH为例)

1.3.1 修改PATH策略选取

1.3.2 修改环境变量的方法(以PATH为例)

1.4 环境变量的重要性(以PATH为例)

2. 环境变量的分类

2.1 系统中的环境变量及查看

2.2 语言级别理解环境变量

2.3 与环境变量相关的指令

3.本地变量简介

3.1本地变量的定义与查看

3.2本地变量的性质

3.3 指令使用规范总结

4. 在程序中获取环境变量

4.1 命令行参数讲解

4.1.1 理论讲解与基本演示

4.1.2 argc参数的必要性

4.1.3 命令行参数简单应用示例

4.2 环境变量参数对进程的传入

4.3 使用全局变量environ获取

 4.4 使用函数接口获取

5. 环境变量的全局性

5.1 引入概念与铺垫

 5.2 环境变量全局性的体现

5.3 本地变量的局部性

5.3.1 验证本地变量局部性

5.3.2 export修改变量

6. 简单总结


0.前言

在前面三篇博客当中,我们初步了解了什么是进程,fork创建子进程的逻辑,以及对进程状态的详解,讲解进程的优先级,本篇博客我们着重讲解环境变量。同时这也是我们Linux操作系统进程篇的第四篇博客。

本篇博客代码以及相关思维导图都已上传至Gitee码云,请君自取:

practice10 · onlookerzy123456qwq/Linuxtwo - 码云 - 开源中国 (gitee.com)icon-default.png?t=M85Bhttps://gitee.com/onlookerzy123456qwq/linuxtwo/tree/master/practice10如下是本文所讲的知识脉络:

1. 引入环境变量PATH

1.1概念铺垫

Linux操作系统中的环境变量有多个,我们首先从环境变量中的PATH举例,来让大家理解环境变量,而要引入PATH,我们首先看代码的运行。

 我们要执行可执行程序导入内存中成为一个进程,就首先要找到这个可执行文件的路径,只有 路径+可执行文件名 的方式能正确的执行一个可执行文件,这是我们现在观察到的现象。

再明确一个概念,我们编译出的可执行程序(myproc),敲的指令(ls pwd),使用的工具(gcc g++),其实本质都是在硬盘中存储的.exe可执行文件,都是可以通过 路径+可执行文件名的方式 直接导入到内存形成进程去执行。

知道这两个概念后,我们就会产生一个疑问,那就是为什么我们执行gcc , ls这些系统级别的指令,工具(可执行文件)的时候,却不用带路径,可以直接找到这个文件,如我们ls这个可执行文件是存储在/usr/bin里面的,应该用 /usr/bin/ls来执行,但是不加寻址/usr/bin,而直接ls就可找到执行。不过我们自己写的编译出的可执行文件如myproc,a.out等,就必须带路径,如 ./myproc ,/home/zy/103/practice10/myproc ,才可以找到这个文件,只输入可执行文件名myproc,就无法执行了,command not found。 

那这是为什么呢?

那肯定是因为指令,工具这些可执行文件,都是可以被系统默认找到这些文件的存储路径的!那系统是如何设置或找到这些默认路径的呢?这当然靠环境变量!!!

1.2环境变量的查看与设置(以PATH为例)

对于环境变量,我们在系统中的一个查找方式是 echo $ENV_NAME , 如对于环境变量PATH,就可以通过echo $PATH指令来查看。

我们可以看到结果是查看到PATH下有许多的路径信息,事实上,不带路径只带可执行文件名的情况下,系统能够找到一些指令,一些工具,一些可执行程序,就是从环境变量PATH下的默认路径里查找到的

 系统执行可执行程序的规则:

1.如果采用 路径+可执行文件名 的方式,则系统会在指定路径下寻找这个可执行文件去执行,如果没找到就command not found

2.如果只输入 可执行文件名,则系统会从PATH环境变量下的一个个默认路径中依次寻找查看每个默认路径下是否有该可执行文件。若在某个默认路径下找到了该可执行文件,则停止寻找,直接执行

 所以我们自己编译链接出的myproc可执行文件它不在PATH下的默认路径下在一个个默认路径之下找不到这个myproc可执行文件,所以也就出现command not found执行失败。那我们如何让myproc像其他指令工具一样,不带路径就可以执行呢?

1.3 修改环境变量(以PTAH为例)

1.3.1 修改PATH策略选取

我们想让myproc不带路径,直接输入myproc就可以执行,就需要依靠环境变量,使得myproc可以在PATH下的默认路径中能够找到

所以我们就有两种方法:

方法一:把myproc这个可执行文件cp拷贝到PATH下的某个默认路径之下,这样myproc在默认寻址时被找到。

方法一分析:这样其实是不推荐的,因为把一个外来的文件拷贝到一个系统级的路径执行,会污染默认路径下的命名池。

方法二:把myproc所在的路径cp拷贝到PATH下,即在PATH下添加一个新的默认路径,这样myproc在默认寻址的时候,就可以在这个新添加的默认路径中找到myproc。

方法二分析:方法二不会污染原有默认路径的命名池,是一个副作用较小的方法。

再补充一点:我们其实在安装软件到系统中的时候,整个过程其实就是把这个软件拷贝到系统变量下的默认路径,安装的过程就是拷贝的过程。

1.3.2 修改环境变量的方法(以PATH为例)

我们通常是使用export指令添加新的环境变量,或者使用export来修改某个已有环境变量下的内容。下面我们以export修改PATH这个环境变量为例,来看一下export是如何修改PATH环境变量下的内容的。

首先明确一点:在Linux操作系统中,export对环境变量的所有修改,都是内存级别的,只是暂时的,仅本次登录有效,叉掉XShell,再登录之后,环境变量又会变回默认配置

错误示范:

 我们export修改PATH指令不能像上图那样直接给PATH赋值,不然会覆盖原来的PATH下的默认路径内容。不过不用担心,export对环境变量的修改只是内存级别的,重新登录就没事了,当然即使是内存界别,仅本次登录有效的,我们也不推荐过多的修改环境变量

当然如果你想设置修改某个环境变量的内容为永久性的,需要你修改系统的默认配置文件,这个文件在当前用户的工作目录下,vim ~/.bash_profile,里面可以看到各种环境变量,被系统配置的默认内容。当然这个我们也强烈不推荐。

正确示范:

export PATH=$PATH:/home/zy/103/practice10/ 如图修改环境变量PATH的内容成功。

1.4 环境变量的重要性(以PATH为例)

在Windows操作系统下,也是有环境变量这个概念的,例如:

 

环境变量(environment variables),一般是指在操作系统中用来指定操作系统运行环境的一些参数。环境变量通常具有某些重要用途,且环境变量是具有全局性的。

环境变量一个作用体现在链接过程中,如我们在编写C/C++代码后,要使用gcc/g++对源代码编译链接,在链接的时候,其实gcc/g++编译器不知道我们要链接的动静态库的位置在哪里,但是照样可以链接成功形成可执行程序,其中的一个重要原因就是有相关的环境变量在帮助编译器进行查找

2. 环境变量的分类

2.1 系统中的环境变量及查看

在系统当中,环境变量其实不止一个PATH还有HOME,SHELL等等环境变量。比如HOME就是表明了当前用户的工作目录(/home/zy/),SHELL就表明当前使用的shell外壳,即所使用的命令行解释器(bash)。

如下图我们使用echo $指令在系统中查看这些特定环境变量的内容:

如果想要查看系统中的所有的环境变量数据,可以使用env指令进行查看。

2.2 语言级别理解环境变量

我们所定义的一个个环境变量,本质就是OS在内存/磁盘中所开辟的空间,用来存储系统配置相关的一些默认的数据信息

还有一点不要质疑操作系统开辟空间的能力,我们在程序当中int a=0,int b=1,char c=‘A’定义变量及变量内容,本质其实都是进程在问操作系统来给进程开辟一段空间。同样的我们其实也可以类比一下,其实系统中的一个个环境变量PATH,SHELL,HOME,PWD等,其实都可以类比成一个个变量,都是OS所在内存中创造的一个个变量,都有各自的变量名以及变量内容。比如int b=1,变量b里面存储了1,PATH变量里面存储了一个个默认路径内容。

2.3 与环境变量相关的指令

1. echo显示某个特定的环境变量的值

2. export修改已存在的环境变量/设置添加一个新的环境变量

3. env查看所有环境变量

4. unset清除环境变量

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

echo $,env查看环境变量,export修改环境变量我们已经展示,set查看本地变量和环境变量&&unset清除本地变量和环境变量我们会在本地变量中展示。最后也会做一个总结。

所有的特定的某个环境变量都可以被echo $查询到,export可以修改每一个环境变量的值,或添加新的环境变量,unset可以清除任意一个环境变量。env指令仅可以查看系统级别的环境变量,而set指令可以查看本地变量。

下面了解什么是本地变量(当然本地变量也是一种环境变量,所以以上五个指令对全局的/本地变量都或多或少有效。但是全局的环境变量与本地变量之间有着本质的区别,上面各个指令也分别专职针对环境变量和本地变量,最后会总结成一个表格)。

我们这里着重讲解本地性的环境变量,即本地变量,而本篇博文主要体现的是环境变量的全局性,所以后续讲解中,对环境变量主要体现其全局性,本地变量主要体现其本地性。

3.本地变量简介

3.1本地变量的定义与查看

定义本地变量:

我们可以在Linux操作系统中直接定义本地变量。

在命令行解释器上直接定义本地变量及其内容,然后echo $就可以查看这个本地变量的内容。 

查看本地变量可以使用echo $指令,同时也可以使用set指令查看本地变量,而env指令只可以查看系统级别的环境变量,下图中使用env是查看不到定义的本地变量的,同时set不仅可以查看本地变量,也可以查看全局的环境变量。

我们删除本地变量使用unset指令来进行删除,同时unset也可以清除系统级别的环境变量

修改本地变量我们通常直接进行修改(或使用export指令也可以对之进行修改,但有副作用)

*PS:修改本地变量通常直接修改,如果使用export指令修改会将其修改为全局性的环境变量,所以我们通常不使用export指令来修改本地变量。

3.2本地变量的性质

先对比之前讲解的环境变量

环境变量是一个全局变量,在全局有效,每个进程都会受其影响,全局只有一份。

环境变量虽然通过export修改后是内存级别的,暂时性有效,但是通过设置达到永久有效

环境变量本质上是OS在磁盘/内存中开辟的一段空间,存储相应环境变量及其内容。

环境变量是一种系统级别的变量

事实上,系统不仅允许你创造一些系统级别(全局)的变量,同时也允许你创造一些本地变量。比如我们之前看到的PATH,HOME,SHELL,是OS创造的环境变量,比如我们定义的myval1,myval2,这些是我们定义的本地变量。

本地变量的性质:

1. 本地变量是局部变量,只在局部有效,并不能影响全局的进程。

2. 本地变量仅在本次登录有效,下次登录时便“销声匿迹”。

3.3 指令使用规范总结

echo $XXX既可以查看全局环境变量的内容,也可以查看本地变量的值。
env只可以查看系统级别的全局环境变量。
set

既可以查看本地变量,又可以查看全局环境变量。

通常set专门查看本地变量,env专门查看全局环境变量。

export

可以修改已有环境变量的内容,或添加新的环境变量。

注意不要使用export给本地变量改值,不然会修改本地变量为全局的环境变量,本地变量可以直接修改。

unset可以清除本地变量,也可以清除全局环境变量。

4. 在程序中获取环境变量

4.1 命令行参数讲解

4.1.1 理论讲解与基本演示

在我们平时书写程序是,定义的 int main()函数通常是不携带参数的。但是事实上,main函数是可以携带参数的!main函数可以被传参,可以带参数项!

如图我们展现一下main函数通常携带的两个参数:int argc与char* argv[]。

如果你什么都不带,那只会传进去 ./myproc 这个字符串(指针)。所有的字符串,都会无一例外被传入!! 以空格作为分隔符,都被看作字符串传入到进程当中。所以我们有一个字符串指针数组结构的argv参数,里面存储的就是传入的字符串参数,里面有argc个有效字符串(指针)。

这个字符指针数组argv[]里面有argc个有效元素,argv指针数组最后应该元素指向空NULL。

4.1.2 argc参数的必要性

因为最后这个传入的字符串指针数组char* argv[]最后一个元素指向的是NULL,所以for(int i=0;i<argc;++i)其实也就不用了,我们直接for(int i=0;argv[i]!=NULL;++i)也行的,那既然最后一个是NULL, 那代表传入有效参数个数int argc不要不也行吗?

当然argc也是有用的, argv[]这里面一个一个的有效字符串参数,是用户传入的,个数是argc个,如果没有统计好这些个数,需要我们自己遍历统计,影响操作。

而且我们这个可执行程序,需要限制你传入参数的个数,可以直接通过argc知道你传入的字符串参数的个数,比如就可以立即知道:你传入了argc个字符串,你传入了 argc-1 个选项。例如你必须让传入5个字符串参数(必须传入4个选项):

所以argc作用大大滴,我们需要argc代表传入有效参数的个数。

4.1.3 命令行参数简单应用示例

我们的ls,其实是指令其实是可以根据传入不同的参数选项,执行不同的逻辑,如 ls -a -l ,

如 ls -n 等,这其实也是通过程序main函数的传参达成的,下面我们通过一个例子模拟一下这个过程。

#include<stdio.h>
#include<unistd.h>
#include<string.h>
void Usage1(){
  printf("Usage: Only two parameter like 'myproc -a'\n");
}
void Usage2(){
  printf("Usage: Only can choose one of -a -b -h as parameter\n");
}
int main(int argc,char* argv[]){
  if(argc!=2){
    Usage1();
    return 1;
  }
  for(int i=0;i<argc;++i){
    if(strcmp(argv[1],"-a")){
      printf("hello world!\n");
    }
    else if(strcmp(argv[1],"-b")){
      printf("hello CUGers!\n");
    }
    if(strcmp(argv[1],"-h")){
      printf("Everyone can get the goal!\n");
    }
    else{
      Usage2();
      return 2;
    }
  }
  return 0;
}

这里的 myproc 就像 ls。ls,myproc所带的选项的不同都可以执行不同的功能。如 ls 带 -l ,-a ,-n等选项,myproc带 -a ,-h ,-b等选项

指令有很多选项,用来完成同一个命令的不同子功能,选项底层使用的就是我们的命令行参数!!!

现在其实你也可以编译形成一个sort可执行程序 , -q代表快排 ,-b代表冒泡……使用选项代表不同的功能。

4.2 环境变量参数对进程的传入

讲完命令行参数(int argc)char* argv[]的使用之后,我们其实在给main函数传参的时候,main函数也也可以带一个字符串指针数组char* env[], 这其实命令行参数中除argc argv以外的第三个参数!

补充:父进程在fork创建子进程的时候父进程的环境变量也都会传给子进程。在子进程中,子进程在执行它main函数时我们可以通过char* env[]这个参数来获取到父进程创给子进程的环境变量

4.3 使用全局变量environ获取

刚才我们是通过main函数参数在程序中获取该进程被传入的环境变量,当然我们还可以通过第三方变量environ来在程序中获取全局环境变量。

 这个environ类型是一个二级指针char**,事实上,environ所指向的元素是一个个char*的字符串指针,char** environ的结构从某种程度上来说和char* env[]是非常像的

 4.4 使用函数接口获取

使用 getenv 接口,就可以在进程当中,获取某个特定的环境变量

 刚刚的两种方式 char* env[] main函数接收参数   OR   environ全局变量获取

其实都不常用!我们在程序里面获取环境变量用的其实是getenv函数!!

5. 环境变量的全局性

5.1 引入概念与铺垫

我们刚才理解了环境变量的概念,环境变量的数据组织结构,系统中对环境变量、本地变量的区别与指令操作,以及程序中对环境变量的三种获取方式,接下来我们来着重讲解环境变量的全局属性vs本地变量的局部性。

环境变量是具有全局属性,我们可以在全局,任何一个进程当中都能获取到系统中同一份环境变量。那环境变量是如何被每一个进程获取到的呢?

我们首先看一个程序:

 所以我们可以得出一个结论:

命令行上启动的进程,其父进程都是bash !!!!!!这里其实就是bash创建了你这个(子)进程myproc。

 5.2 环境变量全局性的体现

子进程的环境变量从哪里来?那当然都是父进程给子进程传入的父进程在创建子进程的时候,父进程会给子进程传入父进程中的环境变量内容。

其实我们在之前程序中,使用进程的main函数传入的char* env[]参数其实就是父进程传给子进程的全局环境变量。如果main函数不接收这个参数,并不代表子进程就不会接收到父进程的环境变量,事实上父进程也会给子进程传入环境变量,我们可以在子进程的参数列表中看到

bash(命令行解释器)也是一个进程,而通过5.1我们知道:命令行解释器上运行起来的所有进程,其父进程都是bash,所以这些子进程都会被父进程bash传入环境变量。然后这些子进程就都有了系统级别的环境变量

然后这些在命令行bash中创建出来的子进程,自己作为新的父进程,再fork()创建出的子进程,就会再把这一份从bash获得的全局环境变量传承给他们的子进程

从这个意义上说,bash的那一份环境变量,会被无限的一辈一辈的往下传承到其子,孙,重孙子进程....,所以其实系统中所有的进程都可以使用到这同一份环境变量,这其实就是系统级别的环境变量的全局性!!!

bash的那一份环境变量是从哪里来的呢?

bash的环境变量一般是系统给的,也就是说bash(命令行解释器)在登录创建的时候会从系统的配置文件中读取环境变量将全局环境变量导入到自己进程的上下文当中。因为环境变量本身其实就是OS操作系统在系统中开辟的一段空间,并写入存储了基本的环境变量数据。

所以在各个进程当中,我们能够使用char* env[]查到这些环境变量的内容。其实都是shell(Linux下叫bash)从系统的配置文件中读取导入到自己进程的上下文当中,然后再由shell(bash)将自己从系统中读取的环境变量传给从bash上创建的一个个子进程子进程在创建它的子进程的时候,也会将这同一份环境变量,传给子进程的子进程。

所以事实上,我们 ls , pwd , gcc , g++ 这些可以直接输入名字即可执行的指令/工具,他们在在命令行上执行起来时都是一个系统中的进程,这些进程其实都是bash(命令行解释器)的子进程,所以这些指令/工具的进程都会被bash传入全局环境变量,所以这些进程可以使用PATH环境变量,也就是说ls/pwd/gcc/g++/...(指令/工具==可执行文件 -> 进程)可以被

5.3 本地变量的局部性

5.3.1 验证本地变量局部性

刚才我们知道,环境变量具有全局性,全局性的环境变量是可以被父进程传给子进程的。同时我们知道,本地变量是具有局部性的,那本地变量可以被bash(父进程)传给子进程吗?

 说明是本地变量没法继承下来的,即父进程不会把本地变量传给子进程,所以我们说环境变量通常是全局的,本地变量是局部的。

那如何让本地变量mylocalval可以像全局环境变量一样被父进程传给子进程呢?

5.3.2 export修改变量

我们知道,export指令的作用,一是可以对已有的全局环境变量进行修改二是可以设置添加新的全局环境变量。但是如果我们使用export修改本地变量的值,那本地变量的值不仅会被修改,本地变量此时也不再是本地变量,其属性会变为全局性的环境变量,那这样其实mylocalval就可以作为全局的环境变量传给子进程了。

PS:不要拿export指令随意修改本地变量的值,本地变量的值可以直接在命令行进行修改。

6. 简单总结

至此我们对环境变量介绍完毕,在再总结一下,基本概念阶段of环境变量:环境变量就是一堆数据。而且不要质疑OS开辟空间的能力,你在程序中所用的空间基本都是OS给你开辟的,OS当然也可以给自己开辟空间保存系统数据。所以环境变量其实就是OS在磁盘/内存中创建定义的一批数据。这批数据可以被bash来读取到,所以环境变量也被bash读取到,环境变量也被传给子进程中,所以环境变量具有全局性。所以在编译链接的时候,这些子进程就知道了库在哪里,本质上就是环境变量就可以指导相关工具进行查找,下面我们在讲链接的博客中,会细讲。

本博客的内容大体可以用如下思维导图来表现(已放至文章开头Gitee中,君可自取):

 

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值