【Linux】深入理解C语言命令行参数与环境变量

目录

基本概念

命令行参数

环境变量

更多的环境变量

HOME

SHELL

USER

LOGNAME

HISTSIZE

HOSTNAME

PWD

获取环境变量的方法

getenv()

environ

环境变量的特性

export命令

shell脚本


基本概念

命令行参数

*argc\[\]是一个char*的指针数组,argc是该数组中元素的个数

char *argv\[\]是指向一个一个字符串所对应的数组

从我们这里举个例子,将这些值打印出来

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

运行结果如图,以空格为分隔符分开

可能到这里还不能看出什么,我们再举个例子

这段代码,这就是说每次执行都要带上-a或者-b或者-c选项

可能这里还是看不出什么,我们在这个例子上面再添加点东西

命令行参数的用途是为了让一个程序,可以通过选项实现不同的子功能

main的命令行参数,是实现程序不同子功能的方法

bash将该字符串切分

进程启动时,进程拥有一张表,argv表,用来支持实现选项功能

这里发现,我们执行自己的命令是需要带./的,但是使用系统的命令是不需要的

无论是在系统当中还是在哪,我们自己写的二进制程序和指令没有本质的区别,我们用的指令本质就是在系统中预装的二进制程序

环境变量

要执行一个程序,必须先找到它(bash,通过PATH来找<环境变量>):我们执行自己的程序时,要使用./,是因为我们要指明我们的程序在当前路径下,系统的命令不需要是因为系统中存在环境变量,来帮助系统找到目标二进制文件

这是ls存在的路径(也就是系统下)

如果我们将其二进制文件拷贝到/usr/bin/ls这个系统路径下呢,也可以实现跟系统命令一样的效果

就可以像系统命令一样使用了,但是特别不推荐把自己写的二进制文件拷贝到系统当中,因为我们自己写的二进制文件没有经过测试,也没有经过严格的发布流程,代码也没有经过时间的验证,可能存在bug,所以将这些代码放进系统默认的路径下可能污染系统中本来的指令池

所以为什么系统知道执行命令的时候就要去usr/bin目录下面查询呢?

因为系统当中存在环境变量:PATH,默认情况下,是在系统当中存在的,用来标识一段路径,也就是系统中搜索指令的默认搜索路径

在Linux系统当中,如果你想查看所有环境变量,可以使用env:名字=内容

如果只想查看一个环境变量的内容:$+环境变量名字

然后发现路径和路径之间存在冒号(以绝对路径呈现的),也就说当你要进行一个ls命令时,操作系统不是默认在/usr/bin/路径下,默认是去查你的PATH环境变量(如果在第一个路径没找到,就去第二个路径找,以此类推,如果全都没有找到,就会有命令找不到的提示)

归根结底,在系统内,找到某一个命令,二进制文件,默认是在环境变量PATH所对应的这些子路径下找的

这是把当前路径覆盖到PATH中的内容了,导致很多系统中的命令都是用不了了

如果你不小心把环境变量PATH不小心改了,其实不影响,PATH变量是内存级的变量,是在bash的进程上下文里的,就相当于是bash的内部里malloc了一段空间,然后把环境变量字符串保存起来

需要添加的话,需要将加入的拼接到原有的路径中去

每次重启后,PATH环境变量都会恢复初始值

对此,我们需要给出下面问题:

1.如何理解环境变量呢?存储的角度

名称=内容

这些环境变量的值(包括执行命令时,是谁来系统找这些命令的)是被bash保存起来的

当我们登录的时候,系统就会给创建一个bash进程,bash就必须得从系统中读取所有的环境变量信息,然后在bash进程内部形成一张表(环境变量表:指针数组),这些环境变量是一个个字符串,0指针指向第一个以此类推

当我们输入指令(比如"ls -a -b")时,这个命令行字符串是被bash先拿到,构建命令行参数表,然后解析这个命令,然后bash拿着ls这个命令在环境变量表中找到PATH,然后再根据PATH中一个个路径寻找

bash内部有两张表:环境变量表和命令行参数表

环境变量就是key=value的长字符串

2.环境变量最开始从哪里来的呢?

环境变量是从系统的相关配置文件中来的

Linux系统是存在相关的系统配置文件的,这些系统配置文件中包含所有的环境变量的直接或者间接环境变量的内容,所以我们的bash在进行启动时,会从配置文件中读取所有的环境变量值并在自己内部创建一张表

环境变量的配置信息

系统环境变量的值

.bash_profile会要求用户去加载.bashrc

而.bashrc会要求用户去加载/etc/bashrc

该配置文件是用shell脚本写的

进入/etc/bashrc/

在启动的时候,这里设置了我们当前系统命令行默认所对应的权限掩码

现在将我们自己的code文件加入到配置文件中,code就可以直接执行了,是不是永久有效的呢?

重启一下

这里说明默认被加入到环境变量配置文件中去了

如果Linux系统有10个用户登录呢?

存在10个bash

更多的环境变量

HOME

表明当前用户的家目录

SHELL

代表的是你当前用户登录时是用的哪一个版本的shell

USER

代表的是当前用户是谁

LOGNAME

代表登录的用户是谁

su只是把用户名发生转化,让我们使用root账号,bash不更新LOGNAME和USER,不更新环境变量

su -就相当于让root重新登录了

HISTSIZE

bash默认会将我们历史上在命令行所使用过的命令保存起来,方便我们cirl+r查看历史信息和滚动滑轮上下搜索历史命令

如下图,bash只会记录最新的1000条命令

该指令可以查看历史命令

HOSTNAME

表明当前主机的主机名

PWD

记录我当前的工作路径是什么

获取环境变量的方法

查看所有环境变量

查询某一环境变量(echo+$xxx)

自己想导入一个环境变量(export key=value)

取消一个环境变量(unset+key)

通过代码方式获得环境变量

命令行参数表和环境变量表是父进程(bash )传递给我们的,最后一个元素都是以null结尾的

在Linux系统中,入口函数不是main函数,而是start函数,start函数会调用main函数

编译器会对你程序main函数所带的参数进行扫描

获取的环境变量是父进程的(bash),环境变量可以被子进程继承

以bash为根部开始,所有的子进程都可以继承该环境变量(环境变量在系统中通常具有全局特性)

getenv()

根据环境变量的名字获取环境变量的内容

这样写的原因是这些变量我们定义出来,但我们不用的话,在我们的g++和vs当中,编译器可能会报错报警,这里我们强转一下,这里的变量被应用了,就不会报错报警了

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//main函数最多有几个参数呢?3个
int main(int argc,char *argv[],char *env[])
{
	(void)argc;
	(void)argv;
	(void)env;
	
	char *value = getenv("PATH");
	if(value == NULL) return 1;
	printf("PATH->%s\n",value);
    return 0;
}

现在在整个系统中只有一个"人"知道我的登录用户是谁,就是bash

子进程就可以继承bash的环境变量,对程序进行身份识别

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//main函数最多有几个参数呢?3个
int main(int argc,char *argv[],char *env[])
{
	(void)argc;
	(void)argv;
	(void)env;


	const char *who = getenv("USER");
	if(who == NULL) return 1;

	if(strcmp(who,"luckin") == 0)
		printf("这是正常的运行逻辑!\n");
	else
		printf("用户错误\n");

    return 0;
}

换成root试一下,就会产生报错

为什么让子进程继承环境变量呢?

因为子进程继承了环境变量相关信息,子进程就可以根据环境变量进行个性化操作,比如定制一个只能让自己执行的程序

可以通过环境变量的方式去禁止某个进程运行,就相当于我们可以在我们的bash当中导入一个环境变量(设置一个flag为真为假,为真可以跑)

environ

此处为二级指针,**environ,因为环境变量表是char*的,environ指向第一个元素(全局的)

用之前需要声明,不需要初始化

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

extern char **environ;
//main函数最多有几个参数呢?3个
int main(int argc,char *argv[])
{
	(void)argc;
	(void)argv;

	for(int i = 0;environ[i];i++){
		printf("environ[%d]->%s\n",i,environ[i]);
	}
return 0;
}

环境变量的特性

环境变量是被所有子进程继承的(全局特性)

本地变量,支持在shell中定义变量(需要通过set查询)

取消就是unset

bash会记录两套变量:1.环境变量 2.本地变量

本地变量不会被子进程继承,只在bash内部被使用

bash为什么要存在本地变量呢?

1.支持bash进行某种脚本语言,就需要这种设定

2.很多本地变量是具有特殊用途的

这里的提示符$表明普通用户

对应命令行提示符的格式,bash要进行记录

这是续行

这个i是本地变量,在env中查询不到,可以用export将其变为环境变量

我们的环境变量是在bash中的

export命令

内建命令(build-in command)

这种命令不需要创建子进程,而是让bash自己亲自执行

bash自己调用函数,或者系统调用完成的

shell脚本

这些命令其实是命令行上可以单独执行的命令,如果想要让这些命令批量化一次性全部执行

相关推荐
hdsoft_huge1 小时前
以2026世界杯晋级逻辑,生动拆解SpringBoot软件架构
java·spring boot·后端
Ajie'Blog1 小时前
AI 编程工具怎么选:Claude Code、Cursor、GitHub Copilot 与 Ollama 指南
人工智能·github·copilot
逸A1 小时前
某里v2反混淆 codec 化路上踩到的两个隐蔽坑:被清零的 salt 与 opaque loop bound
javascript·人工智能·目标跟踪
程序员契奇1 小时前
10_Agent的使用OverAllState和RunnableConfig
后端·agent
linux开发之路1 小时前
C++项目推荐:eBPF+调度器性能分析框架
linux·c++·ebpf·火焰图·调度器
2601_959986241 小时前
从移动端看MMarkets(评测类)值得关注吗?
大数据·人工智能
code_pgf1 小时前
多模态技术在主要 AI 应用领域中的应用分析与工程落地建议
人工智能·stable diffusion
用户6919026813391 小时前
Claude Code 的快捷指令来了!带你快速上手cc提升效率!!
人工智能
坏孩子的诺亚方舟1 小时前
FPGA神经网络数学基础0
人工智能·神经网络·线性代数·fpga开发