1.命令行
前⾯写C语⾔时,很少关注过 main 函数的参数,也没有考虑过 main 为什么会有参 数。
实际上在C语⾔中, main 函数⼀共有三个参数,在命令⾏部分先关注前两个参数:
1. argc:表示 main 函数接收到参数个数
2. argv:表示 main 函数接收的参数
根据下⾯的代码观察这两个参数具体的效果:
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("%d\n", argc);
for(int i = 0; i < argc; i++) {
printf("%s\n", argv[i]);
}
}
对应的 Makefile 如下:
TARGET=test
SRC=test.c
$(TARGET):$(SRC)
gcc $^ -o $@ -std=c99
.PHONY:clean
clean:
rm -f $(TARGET)
直接运⾏程序可以看到下⾯的结果:
如果在输入./test
后,再输入一些字符串可以看到下面的结果:
可以看到, argc 的值即为终端上包括 ./test 在内的所有字符串的个数,⽽ ⼀个字符串数组,存储着终端上包括 argv 是 ./test 在内的以空格间隔的字符串,这个数组 的最后⼀个元素就是 NULL 。这就是 main 函数的两个参数。
前⾯提到Linux下的命令都是⽂件,并且因为Linux是C语⾔编写的,所以所有命令 所带有的选项即为存储在 argv 数组中的字符串,⽽根据不同的参数值实现不同的功 能就可以通过类似于下⾯的形式完成:
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
if(argc == 1) {
printf("默认功能\n");
} else if(strcmp(argv[1], "-f1")==0) {
printf("功能1\n");
}
return 0;
}
2.简单介绍命令如何传递
在Linux下输⼊的命令⾸先会被Shell拿到。
前⾯提到⽗进程和⼦进程的代码是共有的,但是⼆者数据是各⾃独⽴的,但是数据独 ⽴实际上只会建⽴在其中⼀个进程修改了数据,如果⼆者都是对变量进⾏只读不写, 那么也没有必要单独为两个进程开辟两个数据空间
对于上⾯的情况亦是如此,直接在终端上执⾏的进程,其⽗进程都是Shell,并且⽗ 进程和⼦进程都只是以只读的⽅式访问 main 函数的参数,所以就不会出现两个数据 空间。
从上⾯的过程中也可以看出,在设计操作系统、编程语⾔等时,相互都是存在依赖关系的
3.环境变量
前⾯提到 main 函数参数实际上有三个,⽽这第三个参数就是所谓的环境变量 env , 与argv ⼀样,env是⼀个字符串数组
使⽤下⾯的代码可以看到其中的内容:
#include <stdio.h>
int main(int argc, char* argv[], char* env[])
{
for(int i = 0; env[i]; i++) {
printf("%s\n", env[i]);
}
return 0;
}
需要注意的是,因为 argc 只能表示 argv 中的参数个数,所以对于循环终⽌条件来 说,因为env的最后⼀个位置时 NULL ,所以读取到 NULL 即可停⽌
运⾏上⾯的代码,可以看到结果是⼀堆字符串,这些字符串就是所谓的环境变量
在Linux中,环境变量和对应的值都是键值对的形式,⼀共有三种⽅式查看系统中的 环境变量:
-
程序中遍历 env 数组
-
env指令
-
使⽤ environ 指针访问
environ 本质是⼀个⼆级指针,其指向的是每⼀个环境变量的地址,所以通过下⾯ 的代码也可以访问到所有的环境变量:
#include <stdio.h>
#include <unistd.h>
// 引入外部变量
extern char** environ;
int main() {
for(int i = 0; environ[i]; i++) {
printf("%s\n", environ[i]);
}
return 0;
}
4.环境变量PATH
PATH指定了命令默认搜索的位置,因为其值中默认是/ /bin,所以命令执⾏是 会默认去/bin⽬录下找,所以前⾯将⾃⼰写的程序移动到/bin路径下就可以运⾏ 是因为默认从/bin路径下搜索
可以使⽤echo $PATH查看当前⽤户的PATH环境变量的值
也可以使⽤echo ${PATH},此处的{}表示限定变量的边界
如果需要修改PATH值,可以使⽤PATH=指定的路径,但是这种⽅式只是修改了位于 内存的系统进程中对应的PATH,⽽不会影响到本地配置⽂件中的PATH,并且这种 ⽅式会覆盖当前系统进程中的PATH值。
通过上述⽅式修改只需要退出当前⽤户再重新登陆即可重新加载对应的配置⽂ 件中的PATH
如果需要修改PATH对应的配置⽂件,可以到⽤户的家⽬录下找到.bash_profile ⽂件,修改其中的PATH值为需要的值即可
5.环境变量HOME
在Linux中,HOME环境变量指定了每⼀个⽤户的家⽬录,对于root⽤户来说,其值 即为/root,对于普通⽤户该值即为/home/⽤户名
使用 echo $HOME 来查看:
在⽤户登录时,⾸先bash会从配置⽂件中加载对应的值到HOME中,此时bash进程 中的cwd就是家⽬录,因为HOME的值为当前⽤户的家⽬录,⽽因为⼤部分运⾏在 bash进程之上的指令都是bash进程的⼦进程,⼦进程会与⽗进程共⽤⼀块数据, 所以⼦进程的cwd与⽗进程的cwd相同。所以如果直接在家⽬录运⾏⼀个普通的程 } 14 序时,查看其 cwd 值可以看到结果与⽗进程相同,例如下⾯的结果:
需要注意,并不是所有的命令都是 bash 进程的⼦进程,例如 cd 命令
cd 命令并不是⼀个单独的进程,⽽是⼀个内建(built-in)命令。所以如果使⽤ cd 改变当前 bash 的⼯作路径, cd 指令通过 chdir 函数对 bash 进程中的数据直接 进⾏修改,从⽽达到直接改变 bash 进程的 cwd ,例如下⾯的效果:
为了更加直观得看到当前 bash 进程的PID ,可以使⽤ echo $$命令,其中的$$ 就代表当前 bash 的 PID
6.环境变量PWD
在Linux中, PWD 中的值为当前⽤户所处的⼯作路径,所以使⽤pwd 命令查看当前的⼯作路径实际上就是在读取 PWD 中的值
PWD ⼀般⽤于进程在当前⼯作路径下创建⼀个⽂件,在标准库中提供了⼀个函数为 getenv ,原型如下:
char *getenv(const char *name); // 参数传递环境变量名,返回环境变量值
只要获取到了当前路径,就可以在当前路径下创建⽂件
如果需要在程序中创建或修改⼀个环境变量,可以使⽤ putenv 函数,原型如下:
int putenv(char *string);// 参数传递一个键值对,如果指定的环境变量已经存在就实现修改,否则就是新增
对应的还有⼀个 OLDPWD 环境变量,该环境变量的值为上⼀次的⼯作路径,这也就是 为什么使⽤ cd -可以切换为上⼀次⼯作路径的原因
7.环境变量 SHELL
在Linux中, SHELL 表示当前使⽤的终端:
8.理解环境变量
环境变量本质是系统提供的具有全局属性的变量,既然是全局属性,则证明所有进程 都可以访问到环境变量,这个过程实际上就是通过进程之间的⽗⼦关系实现的,因为 环境变量本质是被 bash 进程从配置⽂件加载的,所以 bash 进程和其⼦进程就都可 以看到环境变量。在⼤部分情况下环境变量是不会被修改的,所以 程实际上是共⽤⼀块环境变量空间
前⾯提到有⼀些指令时内建指令,另⼀部分是外部命令,常⻅的内建命令还有 echo 和ex port
在Linux中, bash 是 shell 脚本语⾔的⼀种实现,⽽ shell 是⼀个命令⾏解释器, ⽤户通过它与操作系统进⾏交互,它可以运⾏命令、脚本和程序,所以可以在命令⾏ 直接创建⼀个变量,这种变量也被称为本地变量,例如:
本地变量与环境变量基本⼀致,只是本地变量不具有全局属性,即⽗⼦进程不会共享
使⽤set可以查看当前创建的本地变量和环境变量,如果想要清除指定的本地变量可以使⽤unset + 本地变量名
本地变量也可以使⽤ echo 命令输出:
如果需要将本地变量添加到环境变量,可以使⽤ export 命令,例如:
也可以直接将变量的初始化结合 export 使⽤: