【Linux】理解Linux环境变量

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

    🍎作者阿润菜菜
    📖专栏Linux系统编程


    初识环境变量

    什么是环境变量

    我们平常所用的Linux指令其实也是可执行程序和我们自己写的二进制程序没什么两样那么为什么我们在执行自己的程序的时候需要加上 ./而在我们执行系统提供的指令时可执行程序为什么不需要加上./ 呢

    在这里插入图片描述
    其实时我们要执行一个程序或者指令必须先找到这个程序。
    所以我们在执行自己写的程序时要加上./当前路径其实就是为了找到我们所写的程序而系统指令实际上是系统默认帮我们找到其程序的位置并且执行如果想不带./的执行自己的程序那就需要将程序安装安装的本质就是拷贝到/usr/bin目录下面也就是系统安装指令的路径当中。但我们不建议这么做因为未知的程序可能会有污染系统指令池的风险。

    所以什么是环境变量
    概念: 环境变量environment variables一般是指在操作系统中用来指定操作系统运行环境的一些参数
    就是说比如我们在执行C/C++代码代码时会调用语言的库就那在链接的时候我们从来不知道我们所需要链接的动静态库在哪里OS照样可以链接成功生成可执行程序原因就是有 相关 环境变量帮助编译器去进行查找。

    添加程序路径到PATH环境变量里像指令一样运行程序

    像是ls这样的指令程序就是在/usr/bin目录下的那为什么在/usr/bin路径下的程序系统就可以找到呢其实是因为系统里面存在环境变量PATH操作系统在启动的时候会在shell的上下文当中定义一个PATH变量这个变量是全局有效的如果想要查看内容可以利用echo $PATH命令进行查看。

    查看环境变量的方法 echo $NAME //NAME:你的环境变量名称

    在这里插入图片描述

    在执行系统指令的时候系统会默认去PATH环境变量里面的路径查找我们输入的指令程序如果找到系统就会执行这个程序如果没有找到就会报错command not found如果我们想要想要像:ls、pwd、tree等命令一样使用起来不带路径的话我么只需要将我们的可执行程序添加到PTAH中任一一条目录下或者将我们的路径追加到PATH环境变量下面
    另外我们可以任意修改PATH的值因为只要重新退出登录PATH环境变量就又会恢复了

    我们可以用export指令来将shell变量导入到环境变量PATH里面导入的时候需要先将老的环境变量导入进去然后在加上新的路径(追加路径否则会出现你的路径直接覆盖掉之前环境变量PATH里面的所有路径的情况PATH里面的路径下的所有程序都被默认为是系统指令。

    PATH=$PATH:路径

    在这里插入图片描述
    像这样过后我们不仅可以不带路径的使用我们追加的路径下的可执行程序同时也不会影响ls等命令的使用。
    另外我们的环境变量是内存级的所以在你将自己的路径导入到环境变量PATH之后也只是暂时的等你退出xshell之后你的环境变量就又会恢复到默认的样子了
    下面我们来看一些常见的环境变量

    PATH : 指定命令的搜索路径
    HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)

    HOME环境变量记录当前用户的工作目录在具体的哪个路径
    在这里插入图片描述
    cd的本质其实就是shell在解析指令时看到了波浪号shell就会直接调用环境变量HOME的值
    SHELL : 当前Shell,它的值通常是/bin/bash
    在这里插入图片描述
    当然这都是一些比较常见的环境变量我们如果想要查看所有环境变量的话我们可以使用 命令env
    在这里插入图片描述

    进一步理解环境变量

    环境变量的作用

    无论是我们自己写的程序还是操作系统提前给我们准备好的程序想要运行都必须先加载到内存里面因为CPU只能从内存中读取代码和数据但是这里有一个潜在的问题这些程序想要运行都必须让操作系统先找到这些程序找到这些程序才能把他们加载到内存里面.操作系统要找这些程序就必须去特定的路径下面去找这些程序包括系统自带的指令程序和我们自己所写的程序操作系统还有可能找头文件找动态库静态库等等但是为什么你说操作系统能找到这些东西它就能一定找到呢操作系统是如何找到这些东西的

    其实想要找到这些东西操作系统需要做很多的准备工作操作系统在启动的时候他就已经默认从配置文件当中读取了他自己曾经把软件安装到了哪些路径下他把安装到哪些路径下这些重要信息都记录在配置文件里面等到OS启动的时候把配置文件中的这些信息导入到内存里面构建出一个内存级变量这种变量就是环境变量上面所讲的PATH环境变量就是操作系统在启动命令行解释器shell的时候将PATH这样的变量导入到shell的上下文当中当我们执行对应的指令的时候我们就必须通过PATH环境变量里面指定的默认的搜索路径去查找对应的可执行程序所以操作系统为了让我们找到可执行程序其实做了很多的准备工作帮我们定义了许许多多的环境变量通过环境变量帮助我们做了很多本身我们看不见的工作除PATH这个环境变量其实还有很多很多的其他环境变量操作系统需要完成其他很多我们忽略掉的工作这就需要依靠这些它自己定义出来的环境变量这些环境变量都有不同的用途配色方案当前路径主机名用户名历史指令记录默认的shell类型这些都要依靠OS他自己定义出来的环境变量去隐式的解决或处理我们看不到的问题和工作。

    在不同的使用场景下要求操作系统在启动shell之后给我们做命令行解释的时候必须预先设置好一批未来shell可能用到的变量通过这些变量完成我们输入的命令的解释所以 操作系统为了满足不同的应用场景必须预先在自己的OS内设置一大批的全局变量这些全局变量其实就是环境变量!

    环境变量的组织方式

    首先我们知道环境变量是存储的有内容的那么这些环境变量里面的内容是从哪里来的
    答环境变量的内容一般是由os从特定的配置文件中读取到的当我们的os启动完毕过后os就会读取相关配置文件里面的数据放到环境变量里面在Linux下环境变量一般都在家目录下的.bashrc和.bash_profile;
    这些环境变量也是数据os也会对这些数据进行管理os利用一个字符指针数组来维护这个环境变量表
    在这里插入图片描述
    每个程序都会收到一张环境表环境表是一个字符指针数组每个指针指向一个以’\0’结尾的环境字符串这个环境变量表的最后一个元素是NULL以此来表示环境表的长度
    环境变量通常具有全局属性可以被子进程继承下去

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
     char * env = getenv("MYENV");
     if(env){
     printf("%s\n", env);
     }
     return 0;
    }
    

    直接查看发现没有结果说明该环境变量根本不存在.
    然后我们导入环境变量

    export MYENV="Hello World"
    

    再次运行程序发现结果有了说明环境变量是可以被子进程继承下去的!
    在这里插入图片描述
    这是为什么

    如何获取环境变量

    通过代码环境变量主要有3种方式
    1.通过main函数的参数获取:
    我们都知道main函数是可以带参数的而且最多可以带3个参数比如

    int main(int argc,char*argv[],char*envp[]);
    

    其中envp就是指向这个环境表的首元素指针通过envp我们可以获取到环境变量

    #include<stdio.h>
    
    
    int main(int argc,char*argv[],char*envp[])
    {
      int i=0;
      for(;envp[i];i++)
      {
        printf("envp[%d]--->%s\n",i,envp[i]);
      }
    
    
      return 0;
    }
    

    测试结果
    请添加图片描述2.我们也可以不用main的参数C语言给我们提供了一个全局的字符指针用来指向环境表我们也可以通过这个全局的环境表来访问环境变量这个全局的字符指针也就是environ我们只需要声明一下即可使用

    #include<stdio.h>
    
    extern char** environ;
    
    int main()
    {
      int i=0;
      for(;environ[i];i++)
      {
        printf("environ[%d]--->%s\n",i,environ[i]);
      }
    
      return 0;
    }
    
    1. 通过系统调用getenv函数来获取:
      上面的两种方法虽然可行但是用起来实在是不方便比如我向访问一下USER这个环境变量还得我自己去写匹配算法去一个一个寻找很是麻烦而是用getenv函数则会省去我们自己写匹配算法查找的过程我们只需要告诉getenv我们需要获取哪个环境变量即可getenv就会返回对应环境变量的内容如果没有找到对应环境变量则返回NULL
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    	printf("%s\n", getenv("USER"));
    	printf("%s\n", getenv("PATH"));
    	return 0;
    }
    

    在这里插入图片描述
    利用该函数我们可以搞一个只允许私人如IKun使用的pwd命令

    #include <stdio.h>
    #include <stdlib.h>
    #include<string.h>
    #include<assert.h>
    #define NAME "Ikun"
    void Mypwd()
    {
    	char* pwd = getenv("PWD");
    	assert(pwd);
    	printf("%s\n", pwd);
    }
    int main()
    {
    	char* user = getenv("USER");
    	assert(user);
    	if (strcmp(NAME, user) == 0)
    	{
    		Mypwd();
    	}
    	else
    	{
    		printf("\n小黑子...\n");
    	}
    
    	return 0;
    }
    

    为了保持我们的pwd指令能像系统的pwd指令那样执行我们需要将我们的代码所形成的可执行程序添加到PATH其中一个默认目录下
    在这里插入图片描述
    测试
    请添加图片描述

    环境变量和本地变量的关系本地变量包含环境变量

    显示本地变量和环境变量的指令

    1. set指令可以显示shell中的环境变量和非环境变量包括本地变量
    2. env可以显示所有的环境变量

    导入环境变量和取消环境变量

    export MYVAL="youcanseeme"
    unset MYVAL
    

    在这里插入图片描述
    由于自己定义的环境变量默认就是字符串所以在定义的时候既可以带上双引号也可以不带双引号但如果出现定义的环境变量带空格的话就必须带上双引号了所以还是建议在定义的时候带上空格。

    1.父进程shell定义的本地变量不会被子进程继承下去但是父进程的环境变量是会被子进程继承下去的继承的原因就是为了满足不同的应用场景因为许多系统指令ls、whoami、pwd、which、su - 都会涉及到使用环境变量所以这些指令子进程必须继承父进程bash的环境变量以满足不同的使用场景。
    所以环境变量是具有全局性的因为无论是父进程还是子进程都有环境变量子进程的环境变量是从父进程继承得来的。

    2.本地变量只会在当前进程bash内部有效因为它不会被继承下去具有局部性。

    命令行参数

    上面我们再介绍利用代码获取环境变量的时候提到了可以利用main函数的参数来获取其中我们介绍了envp参数但是argc、argv参数是什么呢现在我们就来理解一下这两个参数的意义
    从结构上我们可以看到argv与envp是同一个类型于是我们可以大胆的猜测一下argv也可能表示的是一张表其中argc是这个表的长度
    打印出来看看

    #include <stdio.h>
    
    int main(int argc, char* argv[], char* envp[])
    {
    	int i = 0;
    	for (i=0; i < argc; i++)
    	{
    		printf("argv[%d]---> %s\n", i, argv[i]);
    	}
    	return 0;
    }
    

    1. main函数中的第一个参数是命令行中运行程序的时候字符串的个数以空格为分隔符比如上面运行时-a -b -c等实际上是三个字符串./mycmd也是一个字符串所以argc代表的就是字符串的个数argv指针数组中的指针指向的就是这些字符串通过程序运行结果和代码可以证明这个结论argv数组中打印出来的值实际上就是这些字符串所以main函数中的第二个参数就是命令行参数表表中的指针指向命令行中的所有字符串

    2.所以你看这和我们ls -a -l 的形式是不是一样的实际上我们在命令行指令+选项的方式时shell会读取到这一串字符串然后以空格作为间隔将指令和选项存在一张表里面当Shell为我们的指令创建进程的时候就会将这张表传给子进程子进程就可以根据选项表现出不同的功能

    3. 系统指令其实就是C语言写的程序那么它在带指令运行的时候也能实现不同的功能这是怎么做到的呢实际上在实现的代码中的main函数就是需要argc、argv这样的参数实现的

    4.所以命令行参数最大的意义就是通过不同的参数也就是执行时携带的选项使得进程拥有不同的功能

    类似的在windows下的命令提示符当中我们也可以通过不同的命令行参数来使得进程实现不同的功能例如下面的关机指令可以设置关机时间也可以取消关机选择关机通过-t、-a、-s等参数实现。

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
    标签: linux