我们在使用C语言的时候,经常的写法是int main(){//函数体}。
而且我们也知道它也只是一个函数,当一个进程启动的时候,会有专门的函数来调用这个函数。
那他有没有函数参数呢?其实也是有的,我们今天,就来认识一下main函数的参数。
命令函参数
在Linux系统中我们在命令行总是会使用各种各样的命令,而在命令的后面也会跟一些选项,而且我们知道这些当使用这些命令的时候本质也是启动了一个进程。
这些命令也是用C语言写的,那么它们是怎么实现的的呢?这其实就谈到了main函数的参数,main函数有三个参数,我们先谈前两个参数。
这里面第二个变量是一个指针数组,它里面存储着字符串。第一个变量存储的是argv数组中有多少个字符串。让我们来演示一下,他里面到底会存什么?
那我们这么写呢?
我们发现了,那个指针数组里面存的就是当我们运行这个程序的时候我们命令行输入的东西,而这些东西是用空格来分割开来的,第一个被分割开的字符串就是启动的程序,后面可以就是程序中的选项这里字符串的分割是操作系统帮我们实现的,当操作系统分割之后就会把它通过bash父进程传给我们即将要运行的main函数这个子进程中。其实这也就解释了命令行的命令是如何实现的了。我们现在简单实现一个简易版的计算器。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
if(argc == 4)
{
int a = atoi(argv[2]);
int b = atoi(argv[3]);
if(strcmp(argv[1], "add") == 0)
{
printf("%d + %d = %2d\n", a, b, a+ b);
}
else if(strcmp(argv[1], "sub") == 0)
{
printf("%d - %d = %2d\n", a, b, a - b);
}
else if(strcmp(argv[1], "mul") == 0)
{
printf("%d * %d = %2d\n", a, b, a * b);
}
else if(strcmp(argv[1], "div") == 0)
{
if(b == 0)
{
printf("error:Divided by 0\n");
return 1;
}
printf("%d / %d = %2d\n", a, b, a / b);
}
else
{
printf("option error\n");
printf("availavle options:%s %s %s %s\n", "add", "sub", "mul", "div");
}
}
else
{
printf("use error\n");
printf("correct use:%s\n", "[cmd] [op] [num] [num]");
}
return 0;
}
这里就是利用了命令行参数来实现了自己的一个命令行级别的指令。
现在,我们对命令行中的命令也不陌生了,它们也仅仅是一个程序而已。
但是有人现在就一个个问题了,如果那些指令是程序的话,为什么使用他们的时候不用./来启动呢?这就需要我们介绍下一个知识点,环境变量。
2.环境变量
1). 简单认识环境变量
假如读者使用过Java或者python的ide的话,是会接触到环境变量这么个字眼的。
这个path就是我们在Linux研究的第一个环境变量。查看环境变量的可以这样:这个美元的符号是为了特殊说明PATH
我们看到它里面存储着路径,路径之间用’:'隔开,总共三个路径。其实我们的命令行指令就存在这些路径里,当我们使用指令的时候,系统会自动在这些路径里搜索,搜索到后就启动它们,所以我们把我们的文件移动到这些路径的其中一个,或者是添加我们的路径,一样可以让我们的可执行程序变成ls那样的指令。我在这里演示添加路径的方式:
添加路径如下:
可以看到我们没有使用./就启动了这个程序。而这里的修改方式也是在原有的环境变量上加冒号后再加路径然后再次赋值给PATH。当我们把PATH清空之后,我们会发现基本上所有的命令都不能用了:
我们看到了两种现象,一种是ls、clear这种命令不能用,但是echo可以用,这个问题我们稍后再谈,我们先处理指令不能用了怎么办?很简单,重启虚拟机就可以了。
重启之后,我们发现它又有了。这其实是因为我们没有永久修改环境变量,我们修改的只是内存中的环境变量,当我们重启之后,环境变量又会从磁盘中加载到内存,所以只有改变磁盘中的环境变量文件,才是永久修改,至于怎么永久修改环境变量,也等会儿再说。我们先来认识几个环境变量。
2). 环境变量
首先我们使用env命令可以输出所有的环境变量:
a. USER
其中有一个USER,是显示当前使用系统的用户。
可以看到,当我切换用户的时候,USER也变了。
b. HOME
为什么当我们登录普通账户的时候,进入到的是/home/zsw或者/home/zsw1?而root登陆的时候进入到的是/root?其实就是由于这个环境变量HOME的原因,它会在用户登录系统的时候,先被初始化。
c. PWD
这个我们很熟悉了,显示当前所处路径就是靠它。
d. LANG
当前全局所使用的字符集类型。
3). 获取环境变量的方式
a. getenv
我们上面说了一个env命令可以看到所有的环境变量,我们还有一个系统调用函数getenv。它也可以获取特定的环境变量:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
printf("PATH:%s\n", getenv("PATH"));
return 0;
}
b. main函数参数
还有我们说main函数里有第三个参数,那今天第三个参数就是环境变量!它是存储环境变量的一个数组,linux中以NULL结尾。
c.envrion
envrion是一个其他文件的变量,通过extern关键字可以引入它,它的类型是char** 指向的就是我们上面main函数中环境变量数组。
4. 环境变量的进一步认识
1). 永久修改环境变量
我们在前面说到,我们在命令行中修改环境变量并不是永久修改,而只是修改了内存里的,若要永久修改,需要修改磁盘中的某个文件,因为内存中的环境变量就是从磁盘中的某个文件加载而来的。
这个文件是位于家目录下的一个隐藏文件。
文件里就是关于各种加载环境变量的脚本。不再赘述,若要永久修改,就在这里添加,或者删除就可以。
2). 添加环境变量
我们假如想要添加一个环境变量,我们的方法是:export + 环境变量名=…。
可以看到确实是添加了,那没有export会发生什么呢?
发现它没有报错,其实这种没有加export的,也是变量,但只不过不是环境变量,而是本地变量。
环境变量和本地变量的区别就是,环境变量具有全局属性,通过父进程继承给子进程的方式,而本地变量只在bash内部有效。
Linux命令的进一步分类
我们来看这么一个现象:
那么就有人产生疑问了,那为什么使用echo命令可以输出本地变量的值呢?echo不也应该是一个命令吗?它也应该是一个程序啊,是一个bash的子进程啊。所以它不应该输出本地变量的值才对。
这里就又有了新的定义也回答了上面为什么当我们清空PATH中的路径后echo仍然可以执行,那是因为,在Linux中命令分为两种,一种是常规命令(ls,clear),一种是内建命令(echo,pwd)。常规命令就是bash的子进程,而内建命令你可以理解为是shell的一个函数,那它是shell的函数的话,自然就可以使用本地的变量了啊。
3).关于环境变量的的命令
export导入环境变量
unset 取消变量
set 显示所有变量
echo 显示某个环境变量
env 显示所有环境变量