目录
[方式二:extern char **environ](#方式二:extern char **environ)
前言
本篇博主将会向大家介绍命令行参数和环境变量。向大家介绍这两个知识点是为了后面进程控制以及我们自主做一个简单的shell命令行解释器做准备。这两个知识点本身和进程的关系不大,当前阶段学这些,可以帮你从"只会写单机代码"过渡到"懂得与操作系统交互",让你写出的程序具备通用软件的雏形,并且为你后面学习更硬核的系统编程扫清概念障碍。
一、命令行参数
C语言里,命令行参数本质就是main函数的参数,支持命令行选项的功能。
大家或多或少都见过main函数的这种写法:
cpp
int main(int argc, char *argv[])
{
//...
//...
return 0;
}
参数解读
参数argv\[\]是一个指针数组,数组里的每一个元素都是一个字符串(char *),参数argc指的是argv\[\]里的元素个数。
命令行选项功能
同一个命令,根据不同的选项呈现出不同的功能。比如我们以前使用过的:ls指令,-a选项和-l选项呈现出来的就是不同的功能。
用一个demo理解命令行参数:
cpp
int main(int argc, char *argv[])
{
(void)argc;
if(strcmp(argv[1], "-a") == 0)
{
printf("这是我的程序的功能1\n");
}
else if(strcmp(argv[1], "-b") == 0)
{
printf("这是我的程序的功能2\n");
}
else
{
printf("这是我的程序的默认功能\n");
}
int i = 0;
for(; i < argc; i++)
{
printf("argv[%d]: %s\n", i, argv[i]);
}
}


结论:
每一个进程启动时,bash都会解析命令行参数,生成一张命令行参数表。
作用:
形成命令行参数表是操作系统和软件工程中一个非常经典且核心的设计。一句话概括就是:它是程序在运行时与外界(用户、脚本、其他程序)交互的"标准接口"。
如果没有这张表,你的程序就是一个"死板"的黑盒,只能执行写死在代码里的逻辑。有了它,程序就有了极大的灵活性 。
二、环境变量
1.基本概念
环境变量(environment variables):⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数。
如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪
⾥,但是照样可以链接成功,⽣成可执⾏程序,原因就是有相关环境变量帮助编译器进⾏查找。
环境变量通常具有某些特殊⽤途,还有在系统当中通常具有全局特性。
2.env
我们可以使用env命令查看所有全局变量:

2.常见的环境变量
1.PATH
PATH环境变量里面装的全部都是文件夹的路径,这些路径之间用冒号隔开。它的作用是告诉系统,当你在终端输入一个命令时,应该去哪些文件夹里面寻找这个命令对应的程序。
2.PWD
PWD 环境变量里面保存的是你当前所在的完整绝对路径。换句话说,也就是你此刻在终端里处于哪个文件夹。当你在终端输入 pwd 这个命令并回车时,系统其实就是去读取并打印了 PWD 这个环境变量的值。
3.OLDPWD
OLDPWD 环境变量里面保存的是你上一次所在的目录路径,也就是你用 cd 命令切换目录之前所在的那个位置。
它和 PWD 是成对出现的。每次你在终端里使用 cd 命令切换到一个新目录时,系统会自动把当前目录的位置记录到 OLDPWD 里,然后再把新目录的位置更新到 PWD 里。
这个变量最常用的场景就是终端里的 cd - 命令。当你在终端输入 cd - 时,系统其实就是去读取了 OLDPWD 的值,然后把你带回上一次待过的目录。而且这个命令通常还会自动在屏幕上打印出跳转后的路径,方便你确认位置。
4.HOME
HOME环境变量里面保存的是当前登录用户的家目录的完整路径。
5.HISTSIZE
HISTSIZE环境变量里面保存的是一个数字,这个数字代表当前终端会话能够记住的历史命令的最大条数。
它的作用主要是限制你按上下方向键或者在终端输入history命令时能看到的历史记录长度。当你敲过的命令数量超过这个数字时,系统就会自动把最旧的命令丢掉,只保留最近的HISTSIZE条命令。在大多数Linux系统中,它的默认值通常是1000。
3.echo
我们可以使用echo查看环境变量:

4.疑问:
通过前面学习我们都知道,Linux里的指令本质上也是一个一个的二进制可执行程序。为什么我们写的二进制可执行文件不能向ls、pwd...一样,直接使用文件名打开?
ls 和 pwd 这些系统自带的命令,它们对应的可执行文件早就存放在/usr/bin 目录里面了。而这些目录默认就是写在 PATH 环境变量里的,所以系统瞬间就能找到并执行它们。
而我们用 gcc 或者 make 编译出来的 test 程序,仅仅是保存在你当前所在的文件夹里面。对于系统来说,这个当前文件夹的路径并不在 PATH 的名单里,系统自然不会去那里找,所以直接敲 test 就会提示找不到命令。你加上 ./ 其实就是明确告诉系统,别去 PATH 里挨个文件夹找了,直接在这个当前目录下执行这个文件。
如果你真的想让你写的程序像 ls 一样在任何地方直接输入名字就能运行,很简单,只需要把你编译出来的可执行文件移动或者复制到系统默认的路径里,比如 /usr/local/bin。或者把你存放程序的文件夹路径追加到 PATH 环境变量里就可以了。
使用:sudo cp test /usr/local/bin

5.获取环境变量的内容
在 C 语言程序中,想要访问这个环境变量表,通常有三种方式。第一种是通过 main 函数的第三个参数 envp。你可以把 main 函数写成 int main(int argc, char *argv\[\], char *envp\[\])。这里的 envp 就是指向环境变量表的指针数组。第二种是直接声明并使用系统提供的全局变量 environ。在你的代码里写 extern char **environ 就可以拿到它。第三种也是最常用的方式,是直接调用 C 标准库函数 getenv。你只要传入变量名的字符串,它就会在环境变量表里查找,并返回对应的值。
方式一:envp\[\](环境变量表)
环境变量表是操作系统和程序之间传递配置信息的一个非常核心的机制。简单来说,它就是一个存在于内存中的字符串数组,里面存放着当前进程可以使用的所有环境变量的键值对。
关于环境变量表,可以从以下几个核心方面来理解。
首先是它的存储结构和格式。环境变量表本质上是一个字符指针数组。每一个元素都是一个字符串,格式通常是变量名等于变量值,比如 PATH=/usr/bin 或者 HOME=/home/tmx。这个数组的最后一个元素是一个空指针,用来标记数组的结束。
其次是它存在于哪里,以及是如何传递的。当你登录系统或者打开终端时,系统会为你当前这个进程建立一份环境变量表。之后,当你在这个终端里运行任何程序,比如你写的 test,或者系统命令 ls,启动这个新程序的过程也就是创建子进程的过程,父进程会把这份环境变量表完整地复制给子进程。因此,所有的进程都会从它们的父进程那里继承环境变量表。

cpp
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, char *argv[],char *envp[])
{
int i=0;
for(;envp[i];i++)
{
printf("envp[%d]:%s\n",i,envp[i]);
}
return 0;
}

方式二:extern char **environ
声明了这个environ,该进程直接从bash中获取环境变量表。

cpp
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
extern char** environ;
int i=0;
for(;environ[i];i++)
{
printf("environ[%d]:%s\n",i,environ[i]);
}
}
方式三:getenv

cpp
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
printf("%s\n",getenv("PATH"));
printf("%s\n",getenv("HOME"));
printf("%s\n",getenv("PWD"));
printf("%s\n",getenv("OLDPWD"));
printf("%s\n",getenv("HISTSIZE"));
printf("%s\n",getenv("SHELL"));
return 0;
}
运行结果:

三、总结
任何进程启动时自身都会存在两张表,第一张表是命令行参数表,第二张表是环境变量表。
当用户在终端输入命令字符串并按下回车时,Shell 进程比如 bash 会先对这一整串字符进行处理。它不仅仅是简单地按空格切分,还会处理引号,把带有空格的字符串当成一个整体,以及展开通配符。处理完之后,bash 会在内存中构建一个字符串数组,也就是命令行参数表。通常,数组的第一个元素是程序自己的名字,后面跟着的是用户输入的各个参数,最后一个元素是一个空指针标记结束。构建好这张表后,bash 会把它传递给新启动的进程。这张表是专门为这一次运行定制的,每次运行程序可能都不一样,告诉程序这次要处理什么任务。
而环境变量表则不同。当一个子进程被创建时,它默认会继承父进程的环境变量表。这意味着你在 bash 中设置的环境变量,在你启动的任何程序里都能读到。环境变量表也是一个字符串数组,里面的格式通常是变量名等于变量值,比如 HOME 或者 PATH。这张表相当于是家族传承的全局配置,告诉程序当前的运行环境是怎样的,程序不需要把一些易变的配置写死在代码里,实现了解耦。
简单来说,命令行参数表决定了程序这一次要做什么,是用户直接下达的具体指令;而环境变量表提供了程序运行的基础配置,是父进程传下来的背景环境。两者配合,让程序能够灵活地运行。在编写 C 语言程序时,主函数的标准写法本身就能够直接接收这两张表的信息。
创作不易,还请大家一键三连~