Linux:环境变量

发布于:2024-04-20 ⋅ 阅读:(27) ⋅ 点赞:(0)


环境变量概念

环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数

在编程中,我们可以定义变量,而定义变量的本质,就是在内存开辟一块空间。

在程序运行的过程中,也可以定义变量,Linux操作系统本身就是一个用C语言写的程序,因此操作系统可以在运行的过程中开辟空间环境变量的本质,就是操作系统运行过程中,为自己开辟的空间,存储了一些重要的信息

现在来思考一个问题,我们在执行指令的时候,可以直接输入指令名,但是我们自己的可执行程序,一定要加上绝对路径或者相对路径,这是为什么?

PATH:一个环境变量,存储着多个路径,在这些路径下面的可执行程序,可以直接执行

PATH是我们讲解的第一个重要环境变量,我们现在尝试观察这个环境变量:

echo $xxx:查看xxx环境变量的内容

现在我们执行指令echo $PATH来查看PATH环境变量的内容:

在这里插入图片描述

可以看出,其是由多个路径组成的,每两个路径之间由一个冒号:隔开。

当在命令行输入指令时,操作系统会默认到这些路径下去查找,如果都没有找到,就会报错。

因此我们只需要把自己的可执行程序放到这里面的某一个路径下面,我们自己写的可执行程序也可以当作指令来执行了。而这个工作,就是把自己写的软件安装到系统中。

我们也可以想办法把自己的路径加到PATH环境变量中。

xxx=:修改xxx环境变量的值

现在当前目录下有一个可执行程序test.exe,使用./test.exe后输出hello world

在这里插入图片描述

该可执行程序处于路径/home/box-he/CSDN/process/env下,我们将这个路径加到PATH中,执行指令PATH=/home/box-he/CSDN/process/env

在这里插入图片描述

现在我们就可以直接执行指令test.exe了,但是由于PATH的其它路径都被这个路径覆盖了,我们原有的lspwdmkdir等等指令,都执行不了了。

但是别慌,此时我们只需要关闭xshell,然后重启,此时PATH就会恢复原先的值。至于为什么,我们后面再详细讲解。

我们再来看几个重要的环境变量:

USER:记录当前的用户
PWD:记录当前路径
HOME:记录家目录

在当前环境下,它们的值如下:

在这里插入图片描述

我们能否自己定义环境变量呢?

export xxx=:定义xxx环境变量

比如我定义一个环境变量Linux,其内容为Linux Is Not unix

在这里插入图片描述

此时echo $Linux就可以输出目标语句了,不过自己定义的环境变量,在xshell重启的时候,也会失效。

最后要讲解的一个环境变量基础操作,是env指令:

env:输出所有环境变量

使用指令env

在这里插入图片描述

窗口大小限制,这里只列举了一部分环境变量,但是我们可以看到几个熟悉的环境变量,比如PWDPATH等。


查看环境变量

我们先前说过,可以通过env来查看所有环境变量,也可以通过echo $xxx来查看单个环境变量,但是这些都是在命令行中操作的,在一个可执行程序中,要如何查看环境变量呢?

getenv

getenv是一个函数,其定义在<stdlib.h>中,功能是:输入一个字符串作为参数,该函数输出该字符串对应的环境变量。

当前test.exe执行如下代码:

#include <stdio.h>      
#include <stdlib.h>      
      
int main()      
{      
    const char* path = getenv("PATH");      
    const char* home = getenv("HOME");      
      
    printf("PATH = %s\n", path);      
    printf("HOME = %s\n", home);    
    
    return 0;    
}      

输出结果:

在这里插入图片描述

可以看到,getenv函数确实可以取出单个环境变量对应的值,这个方式也是最常用的。


environ

environ是一个外部的变量,只需要extern后就可以直接使用,本质是一个char*的数组,也就是说其类型为char* []或者char**

environ的每个元素都是char*指针,指向一个字符串,字符串内部存储的就是环境变量。而environ的最后一个元素是NULL,用于帮助程序员判断什么时候数组到了结尾。

接下来我们在test.exe中执行如下代码,看看environ里面的字符串:

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

输出结果:

在这里插入图片描述

可以发现,每个元素确实都是指向字符串的char*指针,输出后每个元素都对应一个环境变量。


main的参数

也许你听说过,main函数也是有参数的,但是在C/C++学习中,这个参数好像可写可不写,学习环境变量后,我们就可以了解一下这些变量的意义是什么了。

argc & argv

main的前两参数分别是argcargv,传参形式如下:

int main(int argc, char* argv[])  
{}

可见,argcint类型的变量,而argv是一个char*的数组。还记得我们刚刚的environ也是一个char*的数组吗?其实它们两个的结构是一样的。

argv每个元素都是char*类型,分别指向一个字符串,argv的最后一个元素也是NULL,用于标识argv的数组结尾

argc代表了argv中元素的个数,所以我们既可以通过NULL来判断argv结尾,也可以通过argc来判断结尾。

test.exe中执行如下代码,来看看argv中存储了什么:

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

直接执行./test.exe

在这里插入图片描述

现在argv中只有一个元素,即字符串"./test.exe"

./test.exe加几个选项试试,./test.exe -a -b -c

在这里插入图片描述

此时argv多了三个元素,分别是字符串”-a“"-b""-c",也就是说,argv内部存储的是我们执行可执行程序的时候,输入的所有选项?

在随便输入些东西试试,执行./test.exe -std=c99 -std=c++11

在这里插入图片描述

现在字符串又变成了"-std=c99""-std=c++11"了,看来不是巧合。

因此可以得出结论:argv参数内部,存储的是调用可执行程序时,输入的选项

那么argv有什么意义呢?

我们在test.exe中执行以下代码:

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

int main(int argc, char* argv[])    
{              
    bool flagA = false, flagB = false, flagC = false;    
               
    for(int i = 0 ; i < argc; i++)    
    {          
        if (strcmp(argv[i], "-a") == 0)    
            flagA = true;//说明输入了-a选项    
        else if (strcmp(argv[i], "-b") == 0)    
            flagB = true;//说明输入了-b选项    
        else if (strcmp(argv[i], "-c") == 0)    
            flagC = true;//说明输入了-c选项    
    }          
               
    printf("正在执行 test.exe\n");    
               
    if(flagA)    
        printf("功能a执行中...\n");    
    if(flagB)    
        printf("功能b执行中...\n");    
    if(flagC)    
        printf("功能c执行中...\n");    
        
    return 0;                                                                                               
}   

此时程序就可以根据我们输入的选项,来判断要执行哪些功能:

在这里插入图片描述

我们使用的绝大多数指令,都是有大量选项的,我们要通过输入不同的选项,让程序执行不同的功能,而程序就是通过识别argv,来判断用户输入了哪些选项,进而执行特定功能的

在我们向bash输入一大段指令的时候,指令本质就是一个字符串,bash会把字符串拆解为一个个小的字符串,然后把它们整合到一个叫做命令行参数表的东西中,命令行参数表其实就是一个指针数组char* [],而argv参数就可以接收这个bash维护的数组,在程序内部读取。


env

main的第三个参数叫做env,其也是一个char*的指针数组,类型为char* []或者char**

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

到目前为止,相信你已经对char* []这个类型的变量比较敏感了,没错,他也是一个字符串指针数组,并且最后一个元素为NULL

其和各个讲的environ变量是一模一样的,内部存储了环境变量,以及环境变量的值,我们向相同的方式来输出这个env

#include <stdio.h>    

int main(int argc, char* argv[], char* env[])
{
    for(int i = 0; env[i] != NULL; i++)
    {
        printf("env[%d]: %s\n", i, env[i]);
    }

    return 0;
}

输出结果:

在这里插入图片描述

我们又看到了这张熟悉的环境变量表,甚至我们可以发现:指令env,外部变量envrionmain的第三个参数env,它们输出的结果都是一模一样的!

bash会维护一张环境变量表,存储着各种环境变量,而指令env,还有外部类变量envrion,以及main的第三个参数env,它们的环境变量表都是这个bash维护的环境变量表

我们写一个程序验证一下:

#include <stdio.h>    
    
int main(int argc, char* argv[], char* env[])    
{    
    extern char** environ;    
    
    printf("%p\n", &environ[0]);    
    printf("%p\n", &env[0]);    
    
    return 0;                                                                                             
}    

我们取出了main的第三个参数env的首元素地址,以及外部变量envrion的首元素地址。

输出结果:

在这里插入图片描述

可以看到,两者地址是一样的,说明它们就是同一张表,或者说是同一个数组。即bash维护的环境变量表,而我们echo $xxx,其实本质上也是去这个环境变量表中查找对应的环境变量,并取出值来。


bash的环境变量

其实环境变量是可以继承的,我用一段代码证明:

#include <stdio.h>                                                                                        
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char* argv[], char* env[])    
{    
    pid_t id = fork();    
        
    if(id == 0)//子进程    
    {    
        for(int i = 0; i < 3; i++)    
        {    
            printf("child:  env[%d]: %s\n", i, env[i]);                                                           
        }    
    }    
    else//父进程    
    {    
        for(int i = 0; i < 3; i++)    
        {    
            printf("father: env[%d]: %s\n", i, env[i]);    
        }    
    }    
    
    return 0;    
}    

通过fork创建了一个子进程,然后父子进程分别输出env这个数组的前三个字符串。

输出结果:

在这里插入图片描述

可以看到,父子进程都可以正常使用env,说明子进程是可以继承父进程的环境变量表

所有在命令行调用的进程,都是bash的子进程,因此我们的在命令行调用的进程可以继承到bash的环境变量表

只有我们登录了系统的时候,bash才会被创建,先前我们知道,bash要维护一张环境变量表。那么bash的环境变量是怎么来的呢?

其实这些环境变量是被存在磁盘中的,而我们启动bash的时候,会把这些环境变量从磁盘中拷贝到内存中,组成一个环境变量表。我们访问环境变量,都是在访问内存中的环境变量。我们修改环境变量,也是在修改内存中的环境变量

而当我们重启shell,那么bash就会重新去磁盘拷贝一份环境变量,我们之前对环境变量的所有修改,都没有影响磁盘中的环境变量,因此我们重启的时候,可以重置环境变量

那么环境变量存储在磁盘的哪里呢?

在家目录中,有一个叫做.bash_profile的隐藏文件,其内部存储的就是环境变量。

其内容如下:

在这里插入图片描述

我们可以看到PATH环境变量就在里面,给出了另外一个文件~/.bashrc,说明当前的.bash_profile存储的只是一小部分环境变量,剩下的要去~/.bashrc看。

而其实~/.bashrc内部还链接了其它文件,这个环境变量的文件一层套一层,比较复杂,如果感兴趣,可以去自己探索一下。