深入理解环境变量

前言

指令就是可执行程序,当我们运行自己的可执行程序时,需要用./来指定路径,可是为什么运行指令时不用指定路径呢?这就是环境变量的作用。

一.常见环境变量

环境变量是在程序运行期间需要用到的具有特定功能的一组变量,这些变量从配置文件中获取,存放于shell内部。说白了就是内存里的一些变量,里面存放了一堆字符串。

PATH

执行一个指令的前提是找到对应的可执行程序,执行一个指令时不需要带路径,就是因为系统会首先在默认搜索路径下查找,而这个默认搜索路径就在PATH这个环境变量中存着。要想查看一个环境变量,只需在变量名前加上$符号,然后echo(输出到屏幕)

可以看到,PATH里的内容是一个字符串,里面有很多路径,用冒号分隔,而我们的大多数指令就存放在其中的/usr/bin目录下。

所以要想自己的程序不带路径就能运行,有两种方法,第一是将程序拷贝到PATH中的某个路径下,例如/usr/bin;第二种方法是把可执行程序所在的路径添加到环境变量中去。

如图这就是修改环境变量的方法,直接给它赋值。由于我们是想添加内容,所以要$PATH后面加上我们想要添加的路径,然后要注意分隔符冒号。

PWD

PWD这个环境变量里存放的是当前所在路径,当我们使用pwd指令时,实际上就是读取了PWD的值。

深入理解环境变量_第1张图片

HOME 

每个用户都有自己的家目录,普通用户的家目录是/home/xxx,root的家目录是/root,家目录存放在存放在HOME这个环境变量中深入理解环境变量_第2张图片

为什么普通用户登录成功后,默认处于家目录/home/xxx,root登录成功后就处于/root呢? 

登录的时候:

1.输入用户名和密码

2.认证

3.形成环境变量(不止一个,如PATH,PWD,HOME等)

        根据用户名,初始化HOME=/root 或 /home/xxx

4.cd $HOME

二.查看环境变量

env

深入理解环境变量_第3张图片

注意此时的USER

su  

深入理解环境变量_第4张图片 

可以发现USER没有变化,并且当前所处的目录也不在root的家目录下 ,但是家目录确实已经变成/root了。

su -

深入理解环境变量_第5张图片 可以发现,su-后USER变成root了,并且也处在家目录下。

总结:su只是把用户身份切换成root,而su -是让系统以root身份重新登录

三.在代码中调用函数获取环境变量

#include

char* getenv(char* name)

#include 
#include 
#include 

int main()    
{    
    char* who = getenv("USER");    
    if (strcmp(who, "root") == 0)                                                                                                                                                   
    {                                                                                                                                                  
        printf("my command\n");                                                                                                                        
        printf("my command\n");                                                                                                                        
        printf("my command\n");                                                                                                                        
        printf("my command\n");                                                                                                                        
    }                                                                                                                                                  
    else                                                                                                                                               
    {                                                                                                                                                  
        printf("You are not root, you don't have power!!!\n");
    }
    return 0;
}

 功能:拦截普通用户运行这个程序

四.命令行参数中获取环境变量

我们可能很少使用命令行参数,实际上执行运行程序时我们可以传递一些参数,这些参数能被main函数接收。main函数可以不带参,可以带两个参数,也可以带三个参数,第一个参数是int argc,接收参数的个数,第二个参数是char* argv[],接收参数数组,即字符指针数组。而第三个参数char* env[]接收的就是环境变量。

1.命令行参数表环境变量表

当shell登录时,系统会给shell创建一个环境变量表,也就是一个字符指针数组,每个指针指向一个环境变量。

系统启动程序的时候,可以选择给我们的进程(main函数)提供两张表:

1.命令行参数表:

2.环境变量表

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

深入理解环境变量_第6张图片

打印出来的信息和用使用env打印出来的信息是一模一样的。

2.命令行参数环境变量是由谁传递的

我们在命令行中启动的进程,实质上是shell(bash)的子进程,子进程的命令行参数和环境变量是父进程bash给我们传递的。

bash创建子进程,会获取我们输入的命令行参数,对它进行处理,然后传给子进程,这个好理解。但是,父进程的环境变量又从哪里来的呢?

深入理解环境变量_第7张图片

我们将PATH清空,发现ls,env等命令就无法执行了,但是重新登录,发现指令又能执行了。

实际上,我们更改的是bash进程内部的环境变量信息。每一次登录,都会给我们形成新的bash进程,bash进程自动从某个地方读取信息形成自己的环境变量表信息。这个地方就是脚本配置文件,家目录下的.bash_profile文件。

深入理解环境变量_第8张图片

发现其中还嵌套了.bashrc文件

深入理解环境变量_第9张图片

继续套娃,/etc/bashrc 

深入理解环境变量_第10张图片

这里面的内容可就多了,有100行的内容,各种各样的环境变量信息。

五.手动添加环境变量

我们也可以手动向环境变量表中导入变量

添加一个变量MYENV,赋值为hello,再用env查看环境变量,发现MYENV并不存在。实际上我们创建的ENV叫做本地变量,并没有导入到环境变量表当中。要想将本地变量变成环境变量,只需export一下:

也可以在在定义变量时就导出到环境变量列表:

深入理解环境变量_第11张图片

但是,当我们重新登录时,刚才自定义环境变量也就没有了。因为不管是本地变量还是环境变量,都是保存在bash进程中,是内存中的数据,bash退出,也就什么都没有了。所以如果我们想要定义一个环境变量,并且让它永久存在,就要更改配置文件。

深入理解环境变量_第12张图片

当我们再登录时,新的bash就会读取脚本文件,形成环境变量。

六.环境变量的全局属性

bash的子进程会通过命令行参数的方式接收bash的环境变量,实际上,普通进程不用命令行参数的方式,也有办法将环境变量传下去。所以说,环境变量具有全局属性,可以父传子,子传孙,一直继承下去。

七.使用全局变量获取环境变量

前面说普通进程fork出来的进程也能继承父进程的环境变量,这是怎么做到的呢?

C语言库为进程提供了一个全局指针变量,char** environ,它指向环境变量表,这个变量定义在头文件中。

不管是命令行参数还是环境变量,父进程bash都要先拿到,命令行参数和环境变量都是bash的数据。而bash创建进程也是通过fork创建的,而fork创建子进程,父子进程代码共享,数据以写时拷贝的方式各自私有一份,所以environ这个指针变量,每个进程都能访问到的,有了这个指针,也就能找到那张环境变量表了。

#include 
#include 

int main()    
{    
    extern char** environ;//声明全局变量    
    for (int i = 0; environ[i]; i++)    
    {    
        printf("environ[%d]: %s\n", i, environ[i]);                                                                                                                                 
    }    
    return 0;    
}    

深入理解环境变量_第13张图片

八.小结:获取环境变量的四种方式

1.env指令

环境变量列表是bash自己通过读取脚本配置文件创建的,所以在命令行,也就是bash内部,自然能够访问到环境变量列表 

2.char* env[]接收命令行参数

bash可以以指针方式给子进程传递环境变量列表,main函数用变量env接收命令行参数,所以能够通过这个变量访问到环境变量列表

3.使用environ全局变量

C语言库提供了一个指向环境变量列表的全局指针变量environ,这个变量会被子进程继承下去,所以每个进程都会直接或则间接地继承environ这个全局变量,从而能够找到bash中的环境变量列表,访问环境变量

4.使用getenv函数

此函数用于访问特定的环境变量,参数传入你要获取的环境变量名字。

九. 本地变量和环境变量的区别

本地变量:本地变量只在bash内部有效,不会被子进程继承下去(不在环境变量表中)

环境变量:环境变量通过让所有子进程继承的方式,实现自身的全局属性

十.常规命令与内建命令

这里有一个问题,在bash中定义了一个本地变量a,前面说本地变量不具有全局属性,只在bash内部有效,子进程访问不到。但是echo是一条指令,它也是一个子进程呀,它怎么就能访问到a呢?

再看一个场景:

深入理解环境变量_第14张图片

我们将PATH清空,ls,env这些指令就不能直接跑了,但是为什么echo指令不带路径还能执行呢?

这是因为echo和我们平常使用的命令不一样!!!

Linux的命令分为两类:

1.常规命令:磁盘上存在的可执行程序,shell(bash) fork创建子进程,让子进程执行任务

2.内建命令:shell(bash)的一个函数,不创建子进程,而是调用函数来执行任务

当bash识别到要执行echo命令,它会去调用自己内部的一个函数,这个函数名字就叫echo。由于这个这个函数是shell内部的,当然可以读取shell内部定义的本地变量喽,并且它也不需要什么默认搜索路径,直接调用内部的函数就好啦。

同样,export指令也是一个内建命令,因为如果让子进程去导入环境变量,那么环境变量不就到子进程的内存空间里去了。

当然,系统中的大多数指令都是常规命令,只有一些安全,短小的命令shell才会亲自执行。

十一.和环境变量有关的指令

1. echo: 显示某个变量值(本地变量和环境变量)

2. export: 导出一个新的环境变量

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

4. unset: 清除某个环境变量

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

你可能感兴趣的:(linux,运维,服务器)