【Linux修炼】9.环境变量

在这里插入图片描述每一个不曾起舞的日子,都是对生命的辜负。

本节目标

掌握环境变量的相关知识。

1. 环境变量

1.1 环境变量的概念

1. 什么是环境变量?

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

2. 为什么会有环境变量?

在Linux系统中,我们发现我们在执行一些指令时,比如ll等指令,直接就可以输入ll指令获得结果,而对于我们自己编译的程序,比如make之后的文件,就需要./文件才能执行。对于执行一个命令来说,我们知道,命令事实上也是一种文件,对于执行这个文件,我们就需要先找到这个文件,在将这个文件执行。而对于ll来说,其对应的文件事实上已经是Linux系统分配到全局的,也就是在默认路径(根目录),因此不需要指定路径搜索这个指令对应的文件,直接ll就代表从根目录查找从而执行。而对于make生成的程序,我们知道这个可执行程序是在当前路径下,并不在根目录中,因此我们也就无法直接输入文件名从而执行,而是需要找到这个文件对应的路径再去执行,因此./就代表着当前路径,而./此文件就代表找到这个文件的路径并且去执行它!

image-20221114184408610

command not found就意味着并没有从根目录找到这个命令对应的文件。

经过上面的描述,我们知道,如果我们将可执行程序也复制到根目录,那么就也可以直接输入文件名就可以执行该程序了。下面演示一下:

image-20221114184828927

但是这么做是不好的,因为我们在根目录下拷贝就相当于下载了一个文件,这样不经过测试的指令程序事实上会污染系统的指令池的,因此为了改善这种方式,就有了环境变量的方式去处理这种问题!

(在此之前,需要删除我们刚才已经拷贝的文件,指令:sudo rm /usr/bin/myprocessimage-20221114185337803删除成功。)

1.2 环境变量PATH

为什么能够在系统中找到对应的指令呢?事实上系统中存在一个环境变量PATH,它能够去检索对应的指令,找到了就会去执行,找不到就会像上面一样提示not found,因此我们如果想用另一种方式去直接执行文件,就可以将这个文件路径添加到环境变量PATH中。

image-20221114190020970

当我们查看PATH变量时,每一个:所间隔的就是一个检索的路径,因此只要把想直接执行的文件的路径也添加到这个里面,就能够检索到从而执行。

当然这里有一个小插曲,如果我们直接通过指令:export PATH=/home/cfy/sbl/lesson13,虽然可以添加到PATH中,但这样的话,PATH中的其他路径也就被覆盖了,这就导致一些系统的指令也用不了,当然这也是可以解决的,我们只需要重新登陆这个系统,就可以恢复过来,因为这些环境变量是保存在内存中的,属于内存中的环境变量。

通过指令 :export PATH=$PATH:/home/cfy/sbl/lesson13就可以将这个指令追加到PATH环境变量中。image-20221114191322290

1.3 其他常见环境变量

  • HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
  • HOSTNAME:主机名
  • USER:当前用户名
  • PWD:当前系统路径
  • HISTSIZE:shell 能记忆的最多历史命令的条数

image-20221114205809695

2. 和环境变量相关的命令

1.echo: 显示某个环境变量值

2.export: 设置一个新的环境变量

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

4.unset: 清除环境变量

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

对于echo和export我们已经知道其具体是如何操作的了,那么对于后几个,我们先来看看env

2.1 env 命令

env: 显示所有环境变量

1.观察内部:

image-20221114195501123

我们通过env命令,就可以显示这些所有的环境变量,事实上是可以通过代码来模拟的,我们拿其中的USER来演示。

2. getenv函数

即我们可以通过调用getenv来调用这里的变量,那我们先来看一下getenv的使用条件及内部参数。

通过man getenv指令:image-20221114200021918

我们发现返回值是char* ,即满足条件返回相应的值,不满足则返回NULL。

那我们可以利用getenv来编辑代码了:

#include<stdio.h>
#include<stdlib.h>
#define USER "USER"
int main()
{
    char* who = getenv(USER);
    printf("user: %s\n", who);
    return 0;
}

image-20221114200307118

即这样就可以获取到对应的身份。此外,如果我们su -到超级用户(root),我们同样执行这个mycmd,他就会出现user: root的结果。因此,在我们执行一些命令的时候,涉及到权限的问题,其中系统就会通过USER来判断你的身份,从而显示你能否调用一些需要权限的指令。

扩展:

image-20221114201620655

我们发现,bash可以直接给变量赋值,但是env并不能查到,因此这种变量只能称为shell当中的本地变量,即所谓的局部变量。我们同样通过代码验证:

 #include<stdio.h>
 #include<stdlib.h>
 #define MY_ENV "myval"
 #define USER "USER"
 int main()
 {
    char *myenv = getenv(MY_ENV);
    if(NULL == myenv)
    {
        printf("%s, not found\n", MY_ENV);
        return 1;
    }
    printf("%s=%s\n", MY_ENV, myenv);      
     return 0;                             
 }                                         

image-20221114202826390

即这样同样说明了在全局中找不到对应的myval。

2.2 export命令

export: 设置一个新的环境变量

在PATH中我们已经知道,export命令可以在PATH环境变量中添加相应的路径,即可以全局查找。但我们看看下面的这种格式的命令:

image-20221114204706710

与之前不同的是,这个指令里面没有=和:等符号格式,但是我们发现,仍然可以通过这种方式使得myval能够查找到。事实上,这并不是将myval添加到了环境变量中,而是因为如下原因:

我们知道,bash是一个系统进程,并且bash进程是所有创建进程的父进程,即我们./mycmd的mycmd在运行时,mycmd也会变成一个进程(fork),而bash就是mycmd的父进程。而环境变量具有全局属性,也就是说环境变量本来就是定义给bash的,但是bash的环境变量会被子进程继承下去(为什么要继承,为了不同的应用场景 ——让bash帮我找指令路径,身份认证),也就是说子进程能够使用bash的环境变量,因此mycmd能够使用bash的环境变量。而myval本地变量能够使用bash的环境变量的前提是只在当前的进程(bash)内有效。

2.3 set命令

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

image-20221114212622332

我们发现,我们所创建的本地变量(局部变量)不能被env找到,但是却可以被set找到,这也就说明了set能够找到shell本地的变量和环境变量,也就是局部变量和全局变量。

而对于set本身,因为里面既有环境变量又有本地变量,因此其内容远多于env,对于类似于续行符 \这样的符号,其内部也存在。

2.4 unset命令

unset: 清除环境变量

我们在上一部分中所创建的本地变量myval仍然存在,虽然是本地变量,但由于bash的存在也就是父进程的存在,可以将此myval看成是环境变量,因此我们才可以将其显示。此外,如果我们想删除环境变量,就可以通过unset的命令删除。(myval从理解上也可作为环境变量被删除,因为bash存在,myval就有bash所对应的属性)能否被删除,这个和本地或者环境变量无关,一个东西能被添加,就一定要能被删除

image-20221114214435557

即这样就将环境变量删除了。

注:对于export定义的本地变量,实际上是字符串类型,并且可以加双引号,也可以不加双引号,但是对于字符串来说,建议加上双引号,因为字符串可能会有空格这种字符。

3. 环境变量的意义

我们拿ls 指令举例:

image-20221115145025184

我们发现,ls 后的指令为什么不需要./,而是直接输入文件名就行了呢?也就是为什么ls就知道这个文件的当前路径在哪里呢?

实际上,这是因为我们有一个环境变量PWD,通过PWD就能够找到对应的路径:image-20221115145436026

我们发现,当我们cd ..到什么路径,PWD就会变成什么路径。因此,ls 就是通过这个环境变量找到了这个文件。

既然环境变量中有PWD,那我们也可以自己实现PWD指令:

#include<stdio.h>
#include<stdlib.h>
#define PWD "PWD"
int main()
{
    printf("PWD = %s",getenv(PWD));              
    return 0;                                
}                                            

image-20221115150058976

如果我们将mycmd拷贝到根目录,那么其就可以变成和pwd一样的功能。

image-20221115150157396

因此,环境变量的意义就在于其可以找到指定文件的路径。环境变量就相当于系统启动时加载到内部的全局变量。image-20221115150512719

最后别忘了将拷贝的文件删掉。

4. 命令行参数

4.1 什么是命令行参数?

在开始之前,大家回想一下,有的编译器中的main函数中存在着这样的参数:image-20221127155926598

比如上面的DevC++中,main()中有着整形的argc参数和指针数组的argv参数,事实上,这两个参数这就是我们这一节中的主角:命令行参数。

那这个所谓的命令行参数到底是个什么鬼东西?有什么作用?能用来做什么呢?有的编译器怎么就没有呢?接下来开始演示:

1. 预备工作:

在演示之前,我们要对其进行准备工作,也就是设置一下Makefile以及程序的代码:

Makefile:

image-20221127160440695

在这里,我们可以通过$脚本语言来改变之前的代码风格,即第一个$后的@代表生成的mycmd,第二个$后的^代表依赖项mycmd.c。而后面的std=c99就是我们演示此命令行参数的必须的指令,只有在c99环境下才能成功编译。

mycmd.c:

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

2. 演示命令行参数:

在程序中加上命令行参数之后,我们以打印的方式看看究竟它对应的是什么东西。执行:image-20221127161200743

我们可以发现,所打印的结果,就是对应的命令行中的命令以及选项,因此我们也知道了为什么叫做命令行参数:

image-20221127161848339

但main函数是程序的入口,那么是谁调用了main函数呢?谁又将命令行参数传给了main函数呢?事实上这些都是shell和操作系统所做出的行为。因此有的编译器中没有并不是真的没有,而是隐藏在相关的位置,操作系统同样会通过命令行参数去通过命令调用这个程序。

4.2 命令行参数的作用

首先,对于我们上面所演示的两个命令行参数,具体有什么作用呢?那我们通过程序看一下:[mycmd.c]

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// ./mycmd /-a /-b /-c
// ./mycmd -ab/-ac/-bc

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        printf("Usage: \n\t%s [-a/-b/-c/-ab/-ac/-bc/-abc]\n", argv[0]);
    }
    if(strcmp("-a", argv[1]) == 0)
    {
        printf("功能a\n");
    }
    if(strcmp("-b", argv[1]) == 0)
    {
        printf("功能b\n");
    }
    if(strcmp("-c", argv[1]) == 0)  
    {  
        printf("功能c\n");
    }
    if(strcmp("-ab", argv[1]) == 0)
    {                                                                                                                                                                            
        printf("功能ab\n");
    }
    if(strcmp("-bc", argv[1]) == 0)
    {
        printf("功能bc\n");
    }
    
    return 0;
}

我们通过这样列出几个选项之后,我们就可以进行如下执行:

image-20221127165154501

我们发现,即通过命令行参数的加入,我们可以通过一个程序的不同选项去实现他不同的功能,那么这就是命令行参数的作用。我们也就知道了对于ls一类的程序是如何通过选项从而去执行不同命令的。

事实上,对于这种【通过不同选项执行一个程序的不同功能】的方式,在windows也可以去执行,当我们通过win+R快捷方式打卡windows的终端,我们输入shutdown /?就可以看到shutdown不同选项所对应的不同功能:image-20221127173810999

我们随便用两种选项举例:1.shutdown -s -t 360 : 电脑在360秒后自动关机:11.27_1

  1. 通过shutdown -a终止这个程序发生,阻止6分钟后关机。image-20221127174314709

    (自己也可以动手试一下)

1. 第三个命令行参数env

当我们明白前两个命令行参数的作用时,事实上还有第三个命令行参数:env。我们知道,env命令能够查看环境变量,在命令行参数中,env也是指针数组类型,因为环境变量实际上也都是一个个的字符串,而最后一个字符串实际上就是NULL,也就是0,因此我们在编辑代码时可以用这个作为截止条件:

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

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

那我们执行一下这个程序:

image-20221127170719857

我们发现所有的环境变量都通过这个程序显示出来,这也就代表着env这个命令行参数确实将环境变量传入了程序中。

2. 验证命令行参数env

那为了验证这个env会将环境变量都传入到程序中,我们就新建一个环境变量export myval=4444,观察运行结果:image-20221127171204243

我们在众多环境变量中发现了myval。

3. 其他显示环境变量的方法

我们知道,除了通过命令行参数显示之外,我们也可以通过getenv获取,但是我们需要明白一件事情,env命令行参数这个指针数组是从哪里传入的环境变量呢?

image-20221127174750156

事实上,env是通过C语言中第三方environ获取的,而environ本身作为二级指针保存,我们知道指针数组和二级指针可以通过解引用的方式相互转换,这也就恰恰对应了可以把environ中的内容一一传递给env,那么我们通过: man environ查看其需要的头文件并演示代码程序:image-20221127172150557

程序【mycmd.c】:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h> 
int main()
{
    extern char** environ;  //声明从文件中获取environ变量
    for(int i = 0; environ[i]; i++)  
    {  
        printf("environ[%d]: %s\n", i, environ[i]);
    }
    return 0;
}

结果:image-20221127172643391

我们同样也能通过这种方法获取到环境变量。

因此,我们知道在进程上下文中三种获取环境变量的方式:getenv函数获取、命令行参数char* env[]获取,第三方extern char** environ获取。其中推荐的是getenv获取,因为可以指定获取。

5. 总结环境变量

通过上面的描述,我们知道,环境变量就是操作系统启动之后将内部内容加载到内存中的变量,也就是全局变量。通过环境变量,我们可以对一些所需要的环境进行配置。

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天都要进步呀~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值