本篇主要解决以下问题:
- 什么是命令行参数
- 命令行参数有什么用
- 环境变量是什么
- 环境变量存在的意义
命令行参数
在学习C
语言中,对于main
函数当初的写法是没有任何参数的,但是实际上,main
函数是可以有参数的,比如下面的写法:
#include <stdio.h> int main(int argc, char* argv[]) { int i = 0; for (i = 0; i < argc; i++) { printf("%d:%s\n", i, argv[i]); } return 0; } • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11
因此这里将内容打印出来,看看这当中是什么内容
程序运行结果如图所示
[test@VM-16-11-centos 10_15]$ vim myproc.c [test@VM-16-11-centos 10_15]$ make gcc -o myproc myproc.c [test@VM-16-11-centos 10_15]$ ./myproc 0:./myproc • 1 • 2 • 3 • 4 • 5
其实,命令行参数是用来支持各种指令级别的命令行选项的设置,例如这里的argv
数组中,存储的字符串其实就是用户写的命令,而如果现在我在运行程序的时候带上选项
[test@VM-16-11-centos 10_15]$ ./myproc -a -b -c -d 0:./myproc 1:-a 2:-b 3:-c 4:-d • 1 • 2 • 3 • 4 • 5 • 6
会发现打印的信息就是以用户写的指令开始进行分割,然后把信息放到数组中,从中其实看出,用户在运行程序的时候写的命令或者是带的选项都可以被main
函数读取并且传参,函数体内部就可以利用这个机制实现不同的选项带来的结果
比如,Linux
中有ls
命令,用来查看文件夹中的内容,如果使用的是ls -a
或者是ls -l
这些选项,就会产生不同的结果,实际上这样的结果也是通过这个原理,通过读取argv
数组中的内容就可以实现各种目的
这里其实可以模拟实现一个touch
命令:
#include <stdio.h> #include <string.h> int main(int argc, char* argv[]) { if (argc == 2) { if (strcmp(argv[0], "./myproc") == 0) { FILE* pf = fopen(argv[1], "w"); if (pf == NULL) { perror("fopen fail\n"); return 1; } fclose(pf); } else { printf("commend error,you should use ./myproc [name]\n"); } } else { printf("commend error,you should use ./myproc [name]\n"); } return 0; } • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11 • 12 • 13 • 14 • 15 • 16 • 17 • 18 • 19 • 20 • 21 • 22 • 23 • 24 • 25 • 26 • 27 • 28
运行结果如下所示:
[test@VM-16-11-centos 10_15]$ ./myproc test.txt [test@VM-16-11-centos 10_15]$ ll total 20 -rw-rw-r-- 1 test test 64 Oct 15 15:36 Makefile -rwxrwxr-x 1 test test 8568 Oct 15 15:57 myproc -rw-rw-r-- 1 test test 602 Oct 15 15:57 myproc.c -rw-rw-r-- 1 test test 0 Oct 15 15:57 test.txt • 1 • 2 • 3 • 4 • 5 • 6 • 7
这样就模拟实现了一个touch
指令,只不过现在还有一个问题,在实际使用touch
指令的时候,我并不需要带前面的这个./
,而是直接可以运行,那么为什么呢?
这就涉及到了环境变量的问题:
环境变量
首先,不管是在什么系统什么环境中,要执行一个命令,必须要找到相应的可执行程序,这是一定的,那么由此可以引出环境变量的概念:
环境变量的概念
- 环境变量(
environment variables
)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:我们在编写C/C++
代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找 - 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
常见的环境变量
PATH
:指定命令的搜索途径HOME
:指定用户的主工作目录SHELL
:当前Shell,一般是/bin/bash
那么现在查看touch命令的位置
[test@VM-16-11-centos 10_15]$ which touch /usr/bin/touch
从中可以看出,touch
命令确实是被放在了这个地方,而正是由于它被放在了这个文件夹内,因此在运行的时候会默认在这个路径下搜索运行,因此,如果我们把前面定义的myproc
程序放到对应的目录下,那么当执行命令的时候,会优先到对应的目录下搜索,因此也就不需要指定是在当前路径,而是会选择去一个默认路径,如果基于这样的理论,就可以找到对应的内容了
[test@VM-16-11-centos 10_15]$ sudo mv myproc /usr/bin [test@VM-16-11-centos 10_15]$ myproc test1.txt [test@VM-16-11-centos 10_15]$ ll total 8 -rw-rw-r-- 1 test test 64 Oct 15 15:36 Makefile -rw-rw-r-- 1 test test 600 Oct 15 16:08 myproc.c -rw-rw-r-- 1 test test 0 Oct 15 16:09 test1.txt -rw-rw-r-- 1 test test 0 Oct 15 15:57 test.txt
从中可以看出,确实创建成功了,也就是说上面的理论是正确的
PATH
在PATH
路径中,存在的路径就是上面所说的默认搜索路径,如果我们能想办法让路径增加呢?也就是说,能否通过增加PATH
中的值,就能达到在默认路径下搜索文件了,在Linux
中存在一个export
命令可以修改PATH
路径,下面来进行执行
[test@VM-16-11-centos 10_15]$ export PATH=$PATH:/home/test/10_15 [test@VM-16-11-centos 10_15]$ myproc test1.txt [test@VM-16-11-centos 10_15]$ ll total 20 -rw-rw-r-- 1 test test 64 Oct 15 15:36 Makefile -rwxrwxr-x 1 test test 8568 Oct 15 16:19 myproc -rw-rw-r-- 1 test test 600 Oct 15 16:08 myproc.c -rw-rw-r-- 1 test test 0 Oct 15 16:21 test1.txt
从中可以看出,已经成功的将PATH
路径添加到这里了,于是在程序的默认搜索路径中就多了/home/test/10_15
路径,bash
会在这个路径下进行搜索执行的命令
但是同时有一个问题,当退出Linux
机器重新访问后,PATH
文件中的路径又恢复成了原来的信息文件,综上可以总结出一些信息:
Linux机器在登陆的时候会发生什么?
- 输入用户名和密码
- 进行认证
- 形成环境变量,比如
PATH
,PWD
,HOME
这些 - 根据用户名进行一定的初始化
cd ¥HOME
这样,就可以根据不同的用户进入不同的目录中,实现不同的权限级别的信息了
环境变量表
在引入环境变量表的概念前,先看环境变量
和环境变量相关的命令:
echo
: 显示某个环境变量值export
: 设置一个新的环境变量env
: 显示所有环境变量unset
: 清除环境变量set
: 显示本地定义的shell变量和环境变量
上图展示了系统中的环境变量,实际上在main
函数的参数中,是有第三个参数的,而这个参数提供的就是环境变量表,也就是说,系统在启动程序的时候,是可以选择给main
函数进程提供两张表的:
- 命令行参数表
- 环境变量表
下面做实验来证明环境变量表的存在性:
#include <stdio.h> int main(int argc, char* argv[], char* env[]) { int i = 0; for (; env[i]; i++) { printf("%d:%s\n", i, env[i]); } return 0; } • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11
此时运行出的结果和前面基本相同,从中也印证了前面说的原理,在进程启动的时候,系统会提供两个表供使用
思考
命令行启动的进程都是shell/bash
的子进程,这是前面进行进程学习的时候知道的,但是问题在于子进程的命令行参数和环境变量是从哪里来的?答案是父进程来的,那么父进程的环境变量信息又是从哪里来的呢?
此时要和前面的内容进行一些联系,为什么每一次重新登陆,都会为用户形成新的bash
解释器,并且新的bash
解释器会形成自己的环境变量表信息呢?这是由于它从配置文件中读取到了这些信息
由此可以得出一个结论:环境变量信息是以脚本配置文件的形式所存在的
每一次登陆的时候,bash
进程会从一个叫做.bash_profile
文件中读取内容,由此为bash
进程创建一张环境变量表信息,由此bash
在创建其他进程的时候,就可以传递环境变量信息了
查看环境变量
# .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/.local/bin:$HOME/bin export PATH • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11 • 12 • 13
本地变量和环境变量
上面最后演示了环境变量的读取文件,那么这里引入了一个新的概念叫做本地变量,那么本地变量应该如何进行理解?
直接用实验来论证:
首先创建一个本地变量:
[test@VM-16-11-centos ~]$ MYENV="hello" [test@VM-16-11-centos ~]$ echo $MYENV hello • 1 • 2 • 3
此时创建出的是一个本地变量,但是本地变量并没有被放到环境变量中,因此在环境变量中也是搜索不到这个本地变量的:
[test@VM-16-11-centos ~]$ env | grep MYENV # 无输出结果 • 1 • 2
此时需要用一个命令:export
->可以将本地命令转到环境变量中去
[test@VM-16-11-centos ~]$ export MYENV [test@VM-16-11-centos ~]$ env | grep MYENV MYENV=hello • 1 • 2 • 3
此时,在环境变量中就找到了这个配置文件,那么在main
函数中是否会存在呢?
[test@VM-16-11-centos 10_18]$ ./myproc |grep MYENV 13:MYENV=hello • 1 • 2
也找到了这个变量,就进一步的论证了,环境变量表是由bash
传递给子进程的
现在重启Linux
机器:
# 找不到内容了 [test@VM-16-11-centos ~]$ env | grep MYENV # 运行结果也找不到对应的内容 [test@VM-16-11-centos ~]$ ~/10_18/myproc | grep MYENV • 1 • 2 • 3 • 4
这是由于,在登陆Linux
服务器后,会根据配置文件中的信息对bash
进行初始化,而此时并没有进行初始化信息,因此就需要对配置文件进行一些修改:
# .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/.local/bin:$HOME/bin # 增加配置信息 MYENV=hello export MYENV export PATH • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11 • 12 • 13 • 14 • 15 • 16
此时继续重新启动
[test@VM-16-11-centos ~]$ env | grep MYENV MYENV=hello [test@VM-16-11-centos ~]$ ~/10_18/myproc | grep MYENV 12:MYENV=hello • 1 • 2 • 3 • 4
找到了对应的内容,由此引出本地变量和环境变量:
**本地变量:**只在bash
进程内部有效,不会被子进程继承下去
**环境变量:**通过让所有子进程继承的方式,实现自身的全局性
由此得出一个结论:环境变量是具有全局性的
命令分类
看下面的实验:
# 将PATH变量设置为空 [test@VM-16-11-centos ~]$ export PATH="" # 以下命令均不能使用 [test@VM-16-11-centos ~]$ ll -bash: ls: No such file or directory [test@VM-16-11-centos ~]$ touch -bash: touch: No such file or directory [test@VM-16-11-centos ~]$ mkdir -bash: mkdir: No such file or directory # pwd仍然可以使用 [test@VM-16-11-centos ~]$ pwd /home/test • 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 9 • 10 • 11 • 12
为什么呢?
在Linux
中的命令分类:
- 常规命令:
shell
通过fork
让子进程执行的 - 内建命令:
shell
命令行的一个函数,可以直接读取shell内部定义的本地变量
而在上面的测试中,像ls
,mkdir
这样的命令,都是shell
通过fork
创建子进程来执行的,而这里的PATH
路径已经被用户破坏了,因此找不到搜索的路径,因此找不见是当然的事,但是为什么pwd
可以找到?这是由于pwd
这样的命令是内建命令,这是shell
命令行的一个函数,可以直接读取shell
内部的本地变量,因此就可以找到对应的值进行输出了
本篇是关于命令行参数和环境变量的,要清楚环境变量的组织形式:
会提供一个environ
指针,这个指针会指向一张环境表,环境表是一个字符指针数组,每一个指针都会有一个字符串