首先,我们思考一个问题,在Linux 环境下,自己写的代码,编译之后,运行的时候为什么要带上 ./ 呢? 实际上这是为了要找到这个文件在哪里。如果使用绝对路径,当然也可以达到同样的结果,如下所示。使用 ./ 只是使用相对路径,找到 a.out 这个文件。
那么我们再看一下平时使用的指令呢。我们使用whereis 指令,找到了 ls 存放的绝对路径,通过绝对路径也可以使用 ls 指令(这个指令本质上就是C语言写的一个程序)。和上面有区别的仅仅是 —— ls 这些指令除了可以通过 绝对路径、相对路径 来使用,也可以不添加任何路径直接使用。
这其实就是已经将这个指令的路径添加到了 Linux 系统的默认路径上,在命令行里面敲好指令并回车,Linux 会自动搜索默认路径, ls 这些指令是在默认路径之下的,所以可以直接执行。但是自己写的程序没有在默认路径之下,所以不可以直接执行。
而这个默认搜索路径就在 PATH 里面,PATH是系统的一个环境变量。使用 echo $PATH 可以查看 PATH 这个环境变量的值。如下,有多个路径,路径之间用冒号分隔,可以找到 /user/bin 这个路径,ls 指令就是在它下面的。
环境变量本质上是一张内存级别的表,当用户登陆系统的时候,这张表会给特定用户形成独属于自己的环境变量表。这张表里面有各种各样的环境变量,分别用于不同的用途:身份认证、动态库查找、确认当前路径等等,每一个环境变量都有自己的用途。
这里先着重介绍环境变量之一 PATH 。
上面已经简单介绍过,PATH 存放的是系统默认搜索路径,当执行一个指令的时候(没有指明路径),会先到 PATH 里面的各个路径去搜索,搜索不到 系统就认为 没有该指令。
但是,如果我把自己编写的程序路径,也添加到 PATH 里面,那么我写的程序,不也就成了一个指令?可以不用添加执行路径。 试 一下!如下,发现确实不用路径就可以执行 a.out ,这样子 a.out 也就相当于一个指令了!!!
注意,这里要使用 export 指令,添加一个环境变量;同时,如果直接 export PATH= /home/leo/coed/test ,就会导致 PATH 的值仅仅只有新添加的,旧的路径会被覆盖,所以要加上原本的路径,$PATH 就是取原本 PATH 的值;然后加上冒号,因为路径是以冒号隔开的。
上面的操作是否略显熟悉?没错,当我们使用 JAVA 语言的时候,在 windows 环境下在 Path 添加路径,和这个一摸一样!!只不过 windows 下只有 P 是大写。Git 也是同理,添加完之后,就可以在命令行里面使用 Git 指令了!!
到这里,以前的很多配置环境变量就豁然开朗,原来如此 ~ 只不过是添加了一个默认搜索路径,让系统可以直接在这些路径下面搜索对应的指令。从而在执行指令的时候,不需要添加该指令所在的路径。
除了 PATH ,系统还会提供其他的环境变量,可以用 env 指令查看所有的环境变量。如下,可以查看到所有的环境变量。
当然了,如果想一个个查看其他的环境变量,也可以通过 echo 和 $ 的组合。如下,分别是查看 USER 、PWD 、HOME 这三个环境变量,分别表示 用户、当前路径、当前用户的家目录 。
上面说到,环境变量是一张内存级别的表,这个表其实是一个字符指针数组,每个指针指向一个以 ‘\0’ 结尾的字符串。大概如下:
在C语言中也可以查看当前进程的环境变量,因为运行一个C语言编译之后的程序,实际上就是运行 bash 创建的一个子进程嘛,该子进程也是有环境变量的(其实是父进程传给子进程的)。
首先,C语言 main 函数的第三个参数,实际上是 指针数组,该数组里面存储的一个个指针 都是指向一个个环境变量的,以 NULL 代表结束。其结构和上面的 <组织方式> 那张图类似。
其次,也可以通过自带的一个 environ 变量来访问,该变量是一个二级指针,指向了当前进程的环境变量表。environ[i] 是指向一个字符串的指针,相当于字符串的首地址。同样的,当 environ[i] 为 NULL,代表结束,后面没有环境变量了。
最后,可以通过C语言自带的 getenv() 函数来获取环境变量的值。
1 #include2 #include3 #include4 #include5 6 int main(int argc,char* argv[],char* envp[])7 {8 9 // 方法一,利用main函数的传参10 int i;11 for(i=0;envp[i];i++)12 {13 printf("envp[%d]->%s\n",i,envp[i]);14 }15 printf("\n\n\n");16 17 // 方法二,利用 environ18 extern char** environ;19 for(i=0;environ[i];i++)20 {21 printf("envp[%d]->%s\n",i,environ[i]);22 } 23 printf("\n\n\n");24 25 // 方法三,利用 getenv() 函数26 char* pwd=getenv("PWD");27 if(pwd == NULL) perror("getrnv");28 else printf("%s\n",pwd);29 30 char* user=getenv("USER");31 if(user == NULL) perror("getenv");32 else printf("%s\n",user);33 34 return 0;35 }
以上面的第一个方法为例(后两个注释掉),编译运行之后如下:
关于第三个方法,其实就可以写出一个权限控制的小程序,如下:
#define NAME "root"char* user=getenv("USER");printf("%s\n",user);if(strcmp(user,NAME) == 0) {printf("这个程序被%s执行!\n",user);}else printf("当前用户%s权限不足,无法执行该程序!\n",user);
如下,是几个和环境变量相关的指令。
export 可以设置新的环境变量,之前的 给 PATH 添加新的路径 就有演示——本质上就是设置一个新的环境变量,也叫PATH,它的值就是原本的路径加上新的路径。
本地变量不具有全局属性,因而它不能被子进程继承。设置本地变量的方式也很简单,如下,变量名 = 变量值即可。
< 变量名 > = value
我们可以用下面的代码来测试本地变量是否能被子进程继承。在 bash 创建变量 HELLO,运行 test 的时候,test 程序实际上就是 bash 的一个子进程。
一开始,设置本地变量 HELLO = 20 ,运行 test ,发现子进程并没有继承 HELLO 这个环境变量;后来使用 export 指令将其设置成全局变量,再运行 test,发现被子进程继承。
1 #include2 #include3 #include4 #include5 6 #define NAME "root"7 8 int main(int argc,char* argv[],char* envp[])9 {10 char* hello=getenv("HELLO");11 printf("我得到的环境变量HELLO的值是:%s \n",hello); 12 13 14 return 0;15 }
上面有提到,在C语言中也可以通过 main 函数的第三个参数来查看环境变量。但是 main 函数的前两个参数也有其用处, argc、argv 是和命令行有关的参数,argc 是统计命令行的命令和参数的总个数,argv 是存储 命令和参数的一张表。
如下图,测试了一下,一开始执行 ./test ,发现只有这一个指令;后来尝试随便加了几个参数,确实如此,argv 存的是父进程传过来的命令行参数。
其过程大致如下图所示。我们输入的一行指令,在 shell 看来其实就是一串字符串,这一串字符串以空格隔开,分成一个个字串存到一张表里面。一般把第一个当作可执行程序,后面的叫做参数选项。(和 ls -a 类似,ls本质上也是一个程序)
这张表是 bash 创建的,bash在执行 test 这个程序的时候,会为其创建一个子进程,同时将这张表传给了子进程,这样 main 函数的 argv 指针实际上就指向了这张表。
由上面的知识,我们实际上就可以自己写一个 根据不同参数 作出不同反应的小程序。
1 #include2 #include3 #include4 #include5 6 #define NAME "root"7 8 void Usage(const char *name)9 {10 printf("\nUsage: %s -[a|b|c]\n\n", name);11 exit(0); // 终止进程12 }13 14 int main(int argc,char* argv[],char* envp[])15 { 16 17 if(argc != 2) Usage(argv[0]);18 if(strcmp(argv[1], "-a") == 0) printf("打印当前目录下的文件名\n");19 else if(strcmp(argv[1], "-b") == 0) printf("打印当前目录下的文件的详细信息\n");20 else if(strcmp(argv[1], "-c") == 0) printf("打印当前目录下的文件名(包含隐藏文件)\n");21 else printf("其他功能,待开发\n"); 22 23 return 0; 24 }