• [Linux](7)环境变量


    环境变量概念

    环境变量(Environment variable)一般指在操作系统中用来指定操作系统运行环境的一些参数。

    例如我们编写的代码在链接的时候,我们并不知道要链接的动态库静态库在哪里,但是照样可以链接成功,原因就是有相关的环境变量帮助编译器进行查找。

    环境变量通常具有某些特殊用途,在系统中通常具有全局特性。

    环境变量初识:PATH

    ❓为什么我们的代码运行要带路径,而系统的指令不用带路径?

    • 因为系统中存在相关的环境变量,保存了程序的搜索路径

    输入 env 查看所有环境变量。

    系统中搜索可执行程序的环境变量叫做 PATH

    [CegghnnoR@VM-4-13-centos ~]$ env | grep PATH
    LD_LIBRARY_PATH=:/home/CegghnnoR/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
    PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/CegghnnoR/.local/bin:/home/CegghnnoR/bin
    
    • 1
    • 2
    • 3

    也可以通过 echo $PATH 查看:

    [CegghnnoR@VM-4-13-centos ~]$ echo $PATH
    /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/CegghnnoR/.local/bin:/home/CegghnnoR/bin
    
    • 1
    • 2

    PATH 内有多个路径,以 : 作为分隔符。

    当我们输入指令后,就会在这些路径中一个一个地搜索,直到找到该指令的可执行程序。

    而我们自己写的程序,不在PATH的路径中,所以要自己带上路径。

    ❓如何能让自己写的程序运行也不带路径呢?

    1. 我们当然可以把自己的可执行程序粘贴到 PATH 的某个路径的底下,不过要在 root 下或者 sudo。我们不建议这样做。

    2. 可以在 PATH 下增加新的路径,export PATH=$PATH:[绝对路径],这样该路径下的可执行程序运行就不用带路径了。

    3. PATH 改错了没关系,退出重新登陆就恢复了。

    常见环境变量

    MANPATH:man 手册的搜索路径

    HOSTNAME:主机名

    SHELL:当前Shell,它的值通常是 /bin/bash

    HISTSIZE:能保存的历史命令条数

    USER:当前用户名

    MAIL:邮箱路径

    PWD:当前所处路径

    LANG:当前采用的编码格式

    HOME:当前用户对应的家目录

    LOGNAME:登陆用户

    相关命令

    echo:显示环境变量

    export:设置一个新的环境变量,或者将本地变量导出为环境变量

    env:显示所有环境变量

    unset:清除环境变量

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

    在命令行中不加 export 直接定义的变量叫做本地变量,它是局部的。

    环境变量的C/C++获取方式

    main函数参数

    我们知道 main 函数可以带两个参数:int argc, char* argv[]

    第二个参数是一个指针数组,第一个参数表示这个数组的大小。

    它们被称作命令行参数,传递的是命令行中输入的程序名和选项。

    我们可以打印里面的内容看一下:

    #include   
    #include   
      
    int main(int argc, char* argv[])  
    {  
        for (int i = 0; i < argc; ++i)  
        {  
            printf("argv[%d]:%s\n", i, argv[i]);
        }
        return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果:

    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
    argv[0]:./mytest
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest -a
    argv[0]:./mytest
    argv[1]:-a
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest -a -b -c
    argv[0]:./mytest
    argv[1]:-a
    argv[2]:-b
    argv[3]:-c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    argv 数组的最后一个以 NULL 结尾。

    利用这个,我们可以实现一个命令行上的计算器:

    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char* argv[])
    {
        if (argc != 4)
        {
            printf("Usage: %s [-a|-s|-m|-d] [one_data] [two_data]\n", argv[0]);
            return 0;
        }
        int x = atoi(argv[2]);
        int y = atoi(argv[3]);
        if (strcmp("-a", argv[1]) == 0)
        {
            printf("%d + %d = %d\n", x, y, x + y);
        }
        else if (strcmp("-s", argv[1]) == 0)
        {
            printf("%d - %d = %d\n", x, y, x - y);
        }
        else if (strcmp("-m", argv[1]) == 0)
        {
            printf("%d * %d = %d\n", x, y, x * y);
        }
        else if (strcmp("-d", argv[1]) == 0 && y != 0)
        {
            printf("%d / %d = %d\n", x, y, x / y);
        }
        else
        {
            printf("Usage: %s [-a|-s|-m|-d] [one_data] [two_data]\n", argv[0]);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    结果:

    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal
    Usage: ./mycal [-a|-s|-m|-d] [one_data] [two_data]
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -a 21 34
    21 + 34 = 55
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -s 21 34
    21 - 34 = -13
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -m 21 34
    21 * 34 = 714
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mycal -d 28 4
    28 / 4 = 7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    那么我们讲这个的意义是什么呢?

    通过这个,我们可以意识到:Linux系统中,同一个命令给与不同的选项会有不同的表现,这也是指令中这么多选项的由来和起作用的方式。


    三种环境变量获取方式

    其实 main 函数还可以带第三个参数 char* env[] ,它表示的就是环境变量

    #include 
    
    int main(int argc, char* argv[], char* env[])
    {
        for (int i = 0; env[i]; ++i)
        {
            printf("env[%d]: %s\n", i, env[i]);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    结果非常长,不过它和我们使用指令 env 显示出来的是一样的。

    这说明:一个进程是会被传入环境变量参数的。

    C语言细节:一个没有参数列表的函数是可以被传入参数的,只不过未被使用。如果参数列表显式写上 void 则无法传参。

    除了上面获得环境变量的方式,我们还可以这样:

    #include 
    
    int main()
    {
        extern char** environ;
        for (int i = 0; environ[i]; ++i)
        {
            printf("%d: %s\n", i, environ[i]);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第三种获得环境变量的方式:

    直接使用 getenv() 函数

    #include 
    #include 
    
    int main()
    {
        char* val = getenv("PATH");
        printf("%s\n", val);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    为什么要获取环境变量?

    因为环境变量中的值是有意义的。

    比如我们可以通过USER环境变量来控制程序的运行权限:

    下面这个程序,只有我能运行 if 语句后面的部分,其他用户都不行(甚至连 root 也不行,除非他改我代码):

    #include 
    #include 
    #include 
    int main()
    {
        char* id = getenv("USER");
        if (strcmp(id, "CegghnnoR") != 0)
        {
            printf("权限拒绝!\n");
            return 0;
        }
        printf("成功执行\n");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    环境变量的全局性

    我们知道,命令行中启动的进程,其父进程都是 bash

    如果我们随便获取一个不存在的环境变量:

    #include 
    #include 
    #include 
    
    int main()
    {
        printf("Hello world, pid:%d, ppid:%d, myenv:%s\n", getpid(), getppid(), getenv("nihao"));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
    Hello world, pid:30517, ppid:26920, myenv:(null)
    
    • 1
    • 2

    结果为null

    CegghnnoR@VM-4-13-centos 2022_8_14]$ nihao=11112222333
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
    Hello world, pid:30921, ppid:26920, myenv:(null)
    
    • 1
    • 2
    • 3

    创建一个本地变量,结果依然为null

    [CegghnnoR@VM-4-13-centos 2022_8_14]$ export nihao
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ ./mytest
    Hello world, pid:31480, ppid:26920, myenv:11112222333
    
    • 1
    • 2
    • 3

    将它导出为环境变量,可以成功显示环境变量的值。

    这说明,环境变量是会被子进程继承下去的。

    而本地变量,本质就是在 bash 内部定义的变量,不会被子进程继承下去。

    残留问题

    定义的本地变量可以使用 echo 打印,export 导出

    [CegghnnoR@VM-4-13-centos 2022_8_14]$ nihao=22
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ echo $nihao
    22
    [CegghnnoR@VM-4-13-centos 2022_8_14]$ export nihao
    
    • 1
    • 2
    • 3
    • 4

    ❓echo 和 export 都是命令,不也是子进程吗,那么它们是怎么获得本地变量的呢?

    其实 Linux 下大部分命令都是通过子进程的方式执行的,

    但是,还有一部分命令,不通过子进程的方式执行,而是由 bash 自己执行(调用自己对应的函数来完成)。

    我们把这种命令叫做内建命令

  • 相关阅读:
    flink1.14 sql基础语法(一) flink sql表查询详解
    chrome高级调试技巧总结
    漏洞复现--IP-guard flexpaper RCE
    2022-08-16 数据库先验性记录
    从实例学Kettle(一):获取股票行情数据
    2022杭电多校(四)
    离子液体1-乙基-3-甲基咪唑六氟磷酸盐([EMIm][PF6])修饰纳米Fe3O4四氧化三铁(规格)
    掌控安全学院SQL注入靶场
    Kotlin高仿微信-第28篇-朋友圈-预览图片、预览小视频
    上位机图像处理和嵌入式模块部署(f407 mcu中的单独上位机烧录方法)
  • 原文地址:https://blog.csdn.net/CegghnnoR/article/details/126343275