环境变量
✳️我们自己形成的可执行程序,我们系统中也有ls pwd top等命令,这些也都是可执行程序,我自己的程序是程序,你也是程序,那么可不可以把我的程序叫做指令,或者命令文件呢?当然可以!我们所写的程序和系统里的命令没有区别!
❓那么为什么我执行pwd/ls回车就行,而自己的程序就要./程序名才行??那么我可不可以向他那样呢?
❓凭什么我执行的时候要带路径,而他执行的时候不需要带路径??
当我们想执行一个程序的时候,会报错“commadn not found”,人话就是执行一个程序,前提是要先找到它!相当于为什么系统的命令找到,而我们自己的程序找不到呢?
原因在于我们系统中是存在相关的环境变量,保存了程序的搜索路径的!
环境变量是系统当中在开机或者用户登陆之时会在系统当中自动形成的一组变量,只要是变量就一定有变量名和变量内容。
Linux存在环境变量,查看的方式是输入命令:#env
我们自己写的找不到原因是:系统中搜索可执行程序的环境变量叫做PATH!
向查看环境变量的内容:#echo $PATH,就可以看到PATH给我们展示出来的一段路径
环境变量PATH会承载多个路径,分别用:作为分割符,我们在执行程序时,比如ls输入后,会在上面的路径当中一个一个去做搜索,只要在特定路径下找到ls,就会执行特定路径下的ls,执行完就停止搜索不再往后走了。
换而言之,我们PATH提供了我们环境变量可执行程序搜索的一个路径。我们当前ls包括很多命令在PATH里面是能够被找到的所以执行的时候可以不带路径。因为我们写的程序不再PATH所展示的路径当中,就不能像ls一样了
✳️那应该怎么做呢? 两种思路。
思路一:把我们的可执行程序拷贝到PATH提供的路径当中。#cp myproc /usr/bin。但是不建议,会污染命令池
思路二:把自己当前可执行程序所处在的路径添加到PATH所展现的路径当中。如何添加呢?
在Linux命令行当中是可以定义变量的,定义变量不只是可以在C/C++当中定义,命令行和系统当中也是可以的。一种:直接#echoa = 100,a叫做本地变量(也是普通变量),#echo $a就可以看到a到值了。 若用命令#env | grep a是查不到的,因为它不是环境变量!
但是输入#export bbbb = 12345,叫做导入环境变量,用,#echo $bbbb便可以查看到bbbb的值了 。
🌟命令行变量分两种:
1.普通变量:你用env | grep a 根本不会查到因为a是本地变量;但是用set | grep a能够看到本地变量a
2.环境变量(全局):因为你是用export导入,所以你用#env | grep a是可以查到的
✳️现在我们会导环境变量了,我们想执行程序不带路径该怎么做呢?很简单把我们当前的环境变量导入到PATH的路径当中去!输入指令#export PATH=…(自己路径),这是错误的,这会删除了PATH里面本身有的路径!但不要害怕这次设计环境变量是有临时性的,重新登录就会重置了。这种操作只会在内存当中做修改,并不改变系统配置的文件内容。
系统当中登陆的配置文件一般在.bash profile/.bashrc里面可以设置。
🌟向PATH里面添加自己的路径,而不是覆盖:export PATH=$PATH:加自己的所写程序的路径。便不会覆盖且可以像ls一样执行程序。
我们系统当中能够执行可执行程序是因为系统当中存在环境变量PATH,他能够帮我们进行路径搜索。
✳️比如我们#export bbb==1234导入了环境变量,若想要取消则用#unset bbb就行
常见环境变量
✳️ echo $HISTSIZE----记录最大数量历史命令
✳️echo $USER—查看用户名(whami类似)
✳️echo $PWD----目前所处路径
✳️echo $HOME—不同用户的家目录
✳️echo $PATH
环境变量的C/C++获取方式
获取环境变量的三种方式
✳️我们的进程想获得环境变量,可以由main函数的第三个参数可以通过它来获得,传进来的值其实就是一个指针数组,指针数组就可以遍历所有的内容
✳️C语言会自动给我们定义第三方变量:extern char** environ;
extern char** environ;
for(int i = 0; environ[i]; i++)
{
printf("%d: %s\n", i, environ[i]);
}
可以获得别人传入的环境变量
✳️有时候我们获取环境变量只想获取环境变量的内容,所以可以叫做
char *val = getenv("PATH");
printf("%s\n", val);-----可以以代码获得环境变量的内容
❓a.我为什么要获取环境变量??—一定有特殊用途!就如下面确认用户登陆类似而已!
比如说我们程序以后需要确认登陆用户是谁,只能由特定的用户登陆才行!
char *id = getenv("USER");
if(strcasecmp(id, "WHB") != 0)
{
printf("权限拒绝!\n");
return 0;
}
printf("成功执行...\n");
❓b.环境变量是谁给我的呢??目前确实谈不清楚,但是我们可以观察到!
main()函数参数
✳️main()函数可以带参数及吗?最多可以带多少?
若带参数则是:int main(int arc, char* argv[])
argc:是命令行参数的个数(包括程序名!)
char* argv[]是什么类型------指针数组。若是数组则有多少个元素?是有argc来决定它能代表数组当中元素的个数;argv包含的就是各种参数。最后都是以nullptr结尾。
那argv里面放什么呢?
for(int I =0; I < arc; I++)
{
printf("argv[%d]: %s\n",args, argv[I]);
}
因为argv最后都是以nullptr结尾,所以可以这样写!
✳️我们在xshell命令行输入:./myproc -a -b中的./myproc是程序名,-a -b是选项,我们最终的选项是字符串,都以一个叫char*[]指针数组的方式分别指向程序名和选项。
✳️我们给main函数传递的argc,char* argv[],我们称之为命令行参数,传递的是,命令行中输入的程序名和选项。
❓意义是什么呢??
若要想写命令行版本的计算器:
./myproc -a 10 20 (假设-a表示的是加法)
10+20=30;
./myproc -s 10 20
10-20=-10
if(argc != 4)
{
printf("Usage: %s [-a|-s|-m|-d] one_data two_data\n",argv[0]);
return 0;
}
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]);
}
🌟意在在于:同一个程序,通过传递不同的参数,让同一个程序有不同的执行逻辑,执行结果。
这就如同ls -al后面会带选项一样,会有不同的表现。
🌟Linux系统中,会根据不同的选项,让不同的命令,可以有不同的表现!指令中那么多选项的由来和起作用的方式!!
✳️main()函数实际上不止可以带两个参数,可以带三个参数!
int main(int args, char* argv[], char* env[])—可以带环境变量
env:指向环境变量的字符串!
for(int i =0 ; env[i]; i++)
{
printf("env[%d]: %s\n", i, env[i]);
}
当我们执行程序的时候,会打印环境变量参数!
✳️我们一个进程是会被传入环境变量的参数的
✳️如果一个C语言函数当前没有设置自己可以带参的情况下,若强行带参数,并不会报错,你所传入的参数依旧会压栈,只不过就是不使用罢了
环境变量通常具有全局属性
✳️我们在命令行定义变量一共有两种:一种是 本地变量(普通变量)aaa= 12无法用#env | grep aaa所查到
另一种是 环境变量 export aaa =123
❓通常说本地变量只在本shell内有效,这是什么意思呢?
我们启动一个程序,查看自己的父进程会发现,父进程的PID是不会变的,我们前面说过了那是bash。那如果kill掉bash呢?则我们正常命令ls pwd等都运行不了了。
我们现在能够正常去使用命令行,是因为这些命令本质上先被我们bash先获得的,bash也是个进程,也有自己的代码,当我们登陆shell的时候,系统就会给我们用户创建bash进程,bash进程使用C/C++写的,我们就可以使用bash当中的cin/scanf等等获得输入的命令,若杀掉了,自然不可以去做命令行解释了。
✳️命令行中启动的进程,父进程全部都是BASH。那么BASH是怎么去启动一个进程的呢?就是用我们之前讲的fork去创建的,至于fork之后怎么把我的程序加载进来让我们程序执行的呢?至于我们子进程是如何被运行传递进我们 命令行参数和环境变量的?由下个课题讲进程控制自己会花50行代码去写一个简单的bash到时候就会深刻理解了!
❓又说环境变量是全局有效,这又是什么意思呢?
我们做实验,在myproc.c获取一个环境变量getenv(qi_104),但是会发现即使我们在命令行上定义了普通变量qi_104=123是无法被我们的程序获取的。但若我们用export qi_104=123导入环境变量,则能被我们的程序所获取!
✳️所以环境变量是会被子进程继承下去的 !!
相当于我们bash开始,我们创建的一大批开枝散叶之后子进程也会创建一大批子进程,如果我在bash就定义一个export环境变量,这个变量就会从定义处开始为起点被以后其他子进程全部拿到,甚至看到,所以我们称环境变量具有全局属性!
✳️所谓本地变量就是在bash内部定义的变量,不会被子进程继承下去。
✳️你在命令行定义local_val = hello–这个是本地变量,还有就是你告诉我们说所有在命令行中启动的程序都是子进程,那么echo也是子进程,echo是一条命令,也是子进程,那么我们在bash父进程内部定义的local_val变量,怎么可能被子进程#echo $local_val读到呢?
再比如export导入环境变量,那么export难道不也是子进程吗,它若是子进程那么它导入的环境变量只会在子进程当中,怎么会在父进程bash 的上下文呢?
✳️解答:Linux大部分命令是通过子进程的方式执行的!但是,还有一部分命令,不通过子进程的方式执行的,而是由bash自己执行(调用自己的对应的函数来完成特定的功能),我们把这种命令叫做内建命令!
我们到时候会手写bash再来整体感受和理解一下!