相信大家在学习java的时候都迷迷糊糊地配置过Path环境变量。那什么是环境变量呢?
下面绿色的test是一个由test.c编译而来的可执行程序,当我们用file查看test和ls命令的时候发现他俩都是可执行程序,
那有没有想过为什么在执行的时候test不能像ls那样直接ls就可以执行了,偏偏要./test
才可以正常执行呢?
这里可以清楚的看到,如果不带路径的话,会显示命令找不到,所以说想要执行一个程序(命令)就要找到这个程序(命令)。
其实ls命令是系统自动帮我们找的,当我们也把这个test1这个文件拷贝到/user/bin/
这个系统默认路径下,那么其实也就可以执行了。
上面介绍了这么多,接下来介绍概念的时候大家也许会更理解什么是环境变量以及环境变量的属性。
➡️环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。这种变量就是操作系统为我们提供的具有全局属性的、往往具备特殊功能的变量。
如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
在Linux中,操作系统在启动bash给我们做命令行解释的时候他就必须得预先设置好一批我们未来可能要用到的变量,这批变量就叫做环境变量。
解释:由冯诺依曼体系我们可以知道,程序(指令)运行的时候必须得先被加载到内存,原因就是因为CPU必须只能从内存里读取代码和数据,所以这里就出现了一个问题程序(指令)要运行起来要被加载到内存的前提条件是得先把这个二进制代码先找到,操作系统找到之后才能加载。那么操作系统要找他就必须得在特定的路径下去找,凭什么操作系统它会找对应的程序(指令)呢?那么其实是需要操作系统要做很多准备工作的,操作系统在启动的时候就已经默认从我们的配置文件当中读取了自己曾经把我们的软件(指令)安装到了哪些路径下,那么操作系统安装到了哪些路径下,就把它记录在配置文件里,让操作系统启动的时候把这个配置文件导到内存里,构建出一个内存级变量这种变量就叫做环境变量。
环境变量是操作系统为了满足不同的应用场景而预先在系统内设置的一大批的全局变量,这些变量实际上在我们的整个系统中,从bash往后一直都会被进程能够访问到。
我们cd ~
,然后ls -al
,就可以看到下面圈住的两个文件
用vim打开.bash_profile
,发现它内部的shell脚本大概意思是如果有.bashrc这个文件那么就启动.bashrc这个隐藏文件。
对于~/.bash_profile比较官方的解释是用户级的环境配置文件,每个用户目录下都会具有各自的,在用户每次登录系统时被读取,里面所有命令都会被shell执行。包括环境变量的配置命令。
对于下面代码:
#include
#include
#define MY_ENV "myval"
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;
}
a.out
是由上面的代码编译而来的。
1️⃣可以发现在没有通过export使myval变成一个全局变量(环境变量)的时候,运行a.out时发现它是找不到myval的,但是在命令行解释器bash上可以通过echo来查看myval的值,说明此时的myval相当于是在bash的进程上的一个局部变量,局部变量只会在当前进程bash内有效。
2️⃣使用export命令使myval变成一个全局变量(环境变量)之后,就可以发现通过运行a.out也可以找到myval的值,那么说明此时环境变量其实是具有的一个全局的属性。
总结:bash是一个系统进程,a.out
运行起来之后也会变成一个进程,而且a.out
是bash的一个子进程,因为环境变量具有全局属性所以会被子进程继承下去。
使用env命令可以看到有一个PWD这样的系统级环境变量,用来标识当前所处的路径。
我们用getenv来获取环境变量。
#include
#include
int main()
{
printf("%s\n", getenv("PWD"));
return 0;
}
编译之后将可执行程序输出到mypwd
中
将mypwd拷贝到系统目录下,我们的mypwd
命令就可以用了。
接下来给大家说一说如何去访问环境变量的问题。
执行程序时,可以从命令行传值给程序,这些值被称为命令行参数。
ls -a -l
在Linux中,ls是程序,后面对应的选项-a -l
就是所谓的命令行参数
C语言中命令行参数是使用 main() 函数参数来处理的,其中:
第一个参数 argc 是一个整型变量表示命令行参数的个数。
第二个参数 argv 是一个指针数组,里面的元素是指向命令行参数的字符指针。
第三个参数 env 是一个指针数组,里面的元素是指向环境变量(字符串)的字符指针。
#include
#include
int main(int argc,char*argv[],char*env[])
{
}
下面的程序就是运用第二个参数的例子。
#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;
}
解释:
每个进程都会收到一张命令行参数表,命令行参数表是一个字符指针数组,最后用NULL结尾,每个指针指向一个以’\0’结尾的字符串。
运行起来之后可以看到,我们输入的命令是./a.out -a -b
,这里就自动去解析,以空格为分割,我们的程序和程序后面对应的选项就会被以字符串的形式填到argv的这个char*类型的指针数组中,然后将其打印出来,假如这里是./a.out -a -b -c
,那么就会自动打印四次。
那么其实在Linux中,对于ls -al
这条命令,ls也就行相当于一个程序,al相当于后面的参数和上面的./a.out -a -b -c
都是类似的,由此可以推断出Linux内部是怎样用不同的指令,不同的选项(参数) 来实现不同的功能的。
➡️下面是通过命令行第三个参数来获取环境变量的代码。
#include
int main(int argc, char* argv[], char* env[])
{
for (int i = 0; env[i]; i++) {
printf("%s\n", env[i]);
}
return 0;
}
运行结果:
解释:
每个进程都会收到一张环境表,环境表是一个字符指针数组,最后用NULL结尾,每个指针指向一个以’\0’结尾的环境字符串。
代码:
#include
int main(int argc, char* argv[])
{
extern char** environ;
for (int i = 0; environ[i]; i++) {
printf("%s\n", environ[i]);
}
return 0;
}
C语言库中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。
上述两种获取环境变量的方法原理一样就是不同的写法罢了。把命令行的第三个参数的所有env写全了,写成environ最后结果也是一样的。
#include
#include
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
常用getenv和putenv函数来访问特定的环境变量。
PATH : 系统默认的搜索路径。通过echo命令查看它的内容,可以看到有很多的路径,而且这些路径都是以:
分隔的。
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录,也就是下面的~
)
SHELL : 当前Shell,它的值通常是/bin/bash。
echo $NAME
//NAME:你的环境变量名称
1️⃣echo: 显示某个环境变量值
可以发现它内部的默认路径以:分隔。
2️⃣export: 设置一个新的环境变量
场景一:一个可执行程序如何变成一个环境变量。
通过export可以在PATH中添加新的环境变量,做完上面的操作就可以发现test1不用带路径,就可以直接运行了。
场景二:在命令行解释器(bash)上定义一个变量,通过export使它变为环境变量。
3️⃣env: 显示所有环境变量
5️⃣set: 显示本地定义的shell变量和环境变量(使用set就会把bash命令行解释器上定义的局部的变量和环境变量都显示出来。)