0


【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/myprocess

image-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>#defineUSER"USER"intmain(){char* who =getenv(USER);printf("user: %s\n", who);return0;}

image-20221114200307118

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

扩展:

image-20221114201620655

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

#include<stdio.h>#include<stdlib.h>#defineMY_ENV"myval"#defineUSER"USER"intmain(){char*myenv =getenv(MY_ENV);if(NULL== myenv){printf("%s, not found\n", MY_ENV);return1;}printf("%s=%s\n", MY_ENV, myenv);return0;}

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>#definePWD"PWD"intmain(){printf("PWD = %s",getenv(PWD));return0;}

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>intmain(int argc,char* argv[]){for(int i=0; i < argc; i++){printf("argv[%d]->%s\n",i,  argv[i]);}return0;}

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/-bcintmain(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");}return0;}

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

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>intmain(int argc,char* argv[]){for(int i=0; env[i]; i++){printf("env[%d]: %s\n", i, env[i]);}return0;}

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

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>intmain(){externchar** environ;//声明从文件中获取environ变量for(int i =0; environ[i]; i++){printf("environ[%d]: %s\n", i, environ[i]);}return0;}

结果:image-20221127172643391

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

因此,我们知道在进程上下文中三种获取环境变量的方式:

getenv

函数获取、命令行参数

char* env[]

获取,第三方

extern char** environ

获取。其中推荐的是getenv获取,因为可以指定获取。

5. 总结环境变量

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

标签: linux 服务器 运维

本文转载自: https://blog.csdn.net/NEFUT/article/details/128067436
版权归原作者 每天都要进步呀~ 所有, 如有侵权,请联系我们删除。

“【Linux修炼】9.环境变量”的评论:

还没有评论