【Linux】进程概念(五) 命令行参数与环境变量的深度解析

【Linux】进程概念(五) 命令行参数与环境变量的深度解析

文章目录


前言:命令行参数数组和环境变量env数组最后一个元素都是NULL。

一、命令行参数

我们先看一段代码:
intmain(int argc,char* argv[]){int i =0;for(i; i < argc; i++){printf("argv[%d]: %s\n", i, argv[i]);}return0;}
运行结果如下:
在这里插入图片描述
上面的main函数的两个参数就是命令行参数,平时我们使用main函数时默认都是没有使用命令行参数的,但其实它是真实存在的。argv是一个字符指针数组(命令行参数表),它指向一个一个的字符串,argc是字符指针数组的元素个数。(arg是参数英文argrments的缩写,c表示count,v表示vector)
那这两个参数起什么作用呢?我们看运行结果可以猜个大概,我们输入的一串指令本质就是字符串,整体字符串会被分为由空格隔开的一个个字串,每个子串会被存放在argv数组中。
这里我相信大家心里都要疑惑,为什么会有命令行参数?还有许多子问题,这里小编不能全部回答,有一些问题需要学到进程控制时才能理解。

为什么有命令行参数?

我们再来看一段代码:
intmain(int argc,char* argv[]){if(argc !=2){//如果不符合命令格式printf("Usage: %s: -a/-b/-c\n", argv[0]);return1;}if(strcmp(argv[1],"-a")==0)printf("这是功能1\n");elseif(strcmp(argv[1],"-b")==0)printf("这是功能2\n");elseif(strcmp(argv[1],"-c")==0)printf("这是功能3\n");elseprintf("功能不支持\n");}
运行结果:
在这里插入图片描述
我们之前学习过,命令本质是程序(大部分是C语言写的),上面代码编译形成的可执行程序myls可以把它理解成一种命令,-a -b -c可以理解成命令的选项。所以我们现在明白了,原来所谓选项本质就是字符串,它可以以一定方式传递给命令程序内部的main函数,在命令程序内部实现的时候,就可以根据不同的选项,实现类似功能的不同表现形式。

命令行参数是由谁做的,命令字符串先被谁拿到?

这里小编不解释太多,我们之前已经知道命令行启动的所有进程都是bash的子进程,那么命令程序自然也是bash的子进程,所以命令字符串会先被bash拿到,然后由父进程bash进行一系列操作把命令字符串打散传给子进程(命令程序),然后子进程就拿到了命令行个数和命令行。具体过程在后面讲程序替换时再细讲。
总结:linux系统中,命令行参数被shell获取时,通常会被父进程(bash)维护成一个指针数组(argv),并提取该数组中有多少有效元素(argc),然后再把argv、argc传递给对应子进程的main函数,子进程拿到后就可以用来实现不同功能了。
补充:argv数组最后一个元素必须以null结尾。下面是示例代码:
intmain(int argc,char* argv[]){int i =0;for(; argv[i]; i++){printf("argv[%d]: %s\n", i, argv[i]);}return0;}
运行结果:
在这里插入图片描述
命令行参数有以下几个特点:
1、命令行参数至少有一个,因为程序要运行至少要有./程序名或者指令名。
2、进程对应的程序名一般是argv[0]。
3、命令字符串有几个由空格隔开的字串,argc就是几。

二、环境变量

环境变量虽然我们没有真正了解过,但是它无处不在,下面我们一起来揭开它的神秘面纱吧。

一个现象引入环境变量

我们之前在学习指令时知道系统级指令可以在任意路径下运行,因为系统级指令可以不加./直接用指令名运行,而我们自己写的可执行程序不做任何修改的情况下只能在可执行程序所在路径下运行,因为系统规定执行自己的可执行程序需要在程序名前加./运行,比如下面:
在这里插入图片描述
当我们直接a.out时系统会报 command not found,这个报错背后意味着系统要找我要执行的程序,我们之前的说法是系统会去user/bin/路径下找,当我们把自己的程序拷到user/bin/路径下后,就可以不加./直接运行了。这里我们就可以详细说一说它背后的原理,user/bin/路径其实被包含在系统内的一个环境变量PATH中,操作系统本质会去环境变量PATH中查找可执行命令。
要证明上面说法我们可以用下面的指令查看环境变量的内容:
在这里插入图片描述
果然user/bin/在PATH路径中,当一个指令在PATH路径中找到了就可以直接运行,如果指令不在PATH路径中我们直接运行系统就会报command not found ,我们自己创建的可执行程序所在的目录(当前目录)默认是不会被PATH路径包含的,所以我们自己的程序默认需要加./运行。
可是linux为什么要这样设计呢?这主要是处于安全考虑,如果当前目录被加入 PATH,当目录中存在伪装成系统命令的恶意程序(比如命名为 ls 的恶意脚本)时,在系统任意路径下执行 ls 会优先运行当前目录的恶意程序,而非系统真正的 ls 命令,存在安全风险。
指令中会用到PATH:
我们之前介绍的which指令本质就是在PATH里找对应的命令:
在这里插入图片描述

修改环境变量

修改环境变量的操作和我们在学习语言时的赋值操作类似,我们下面会分别操作将PATH置为空和将我们自己的路径加入环境变量。
将PATH置为空:
在这里插入图片描述
我们可以看到将PATH置为空后在user/bin/路径下的外部指令就无法使用了,而入cd、pwd等内部指令还能正常使用。但是不用怕,我们退出Xshell然后重新登陆环境变量PATH就正常了,原理我们后面说。
将当前路径加入PATH:
在这里插入图片描述
将当前路径加入PATH后我们的a.out就可以像系统命令一样在任意位置直接运行了。

配环境的本质

在平时我们配java环境、python环境本质就是先将对应的可执行程序下载到电脑里,然后将可执行程序的路径添加到环境变量里,这样可执行程序就可以不局限于可执行程序的路径,而可以在任意路径下运行。

查看环境变量

env指令可以查看系统在当前用户下的所有环境变量:
(小编只挑一部分注释)
在这里插入图片描述

环境变量本质

环境变量有的让用户处于默认家目录(cd ~ == cd $HOME),有的用来记录当前用户是谁(whoami == echo $USER),有的用来记录当前主机名是什么等等,这些环境变量是系统级的全局变量,不同的变量具有不同的用途,它们会在我们用户登陆系统时被天然初始化,我们在任意路径下都可以随时访问。

如何通过代码获取环境变量

1、main函数获取

这里小编要补充一点,环境变量是main函数的第三个参数,前两个main函数参数是命令行参数,我们已经介绍过了,这三个main函数参数都是可以省略的,如果要显示使用第三个参数的话前两个参数也要带上,这是严格要求的。
main函数第三个参数本质也是一个字符指针数组(环境变量表),也是指向一个一个的字符串,这里的字符串是环境变量的KV键值对,如下图所示:(左边小方块environ后面解释)
在这里插入图片描述
我们下面拿代码来验证一下:
//环境变量参数intmain(int argc,char* argv[],char* env[]){int n =0;for(; env[n]; n++){printf("env[%d]: %s\n", n, env[n]);}return0;}
运行结果:
在这里插入图片描述

2、通过函数获取单个环境变量

getenv函数可以获取单个环境变量,使用该函数需要包含stdlib.h头文件。
在这里插入图片描述
getenv函数参数传环境变量名(key),返回值是指向环境变量的具体数据(value)的指针,若环境名参数不存在则返回nullptr。示例如下:
#include<stdlib.h>intmain(){char* path =getenv("PATH");printf("PATH = %s", path);}
运行结果:
在这里插入图片描述

3、通过environ变量获取

C语言为我们提供了一个全局指针变量environ,在linux系统下使用不需要包含unistd.h头文件,只需要声明一下 extern char** environ 即可使用。
在这里插入图片描述
它是一个二级指针,指向了字符指针数组(环境变量表)的首元素地址,既然它是一个二级指针,我们又可以通过environ[]访问整个字符指针数组,示例如下:
intmain(){externchar** environ;int n =0;for(; environ[n]; n++){printf("environ[%d], %s\n", n, environ[n]);}}
运行结果:
在这里插入图片描述

环境变量的来源

上面我们介绍了如何用代码获取当前进程的环境变量,这只是方法,我们还需要知道环境变量从哪来。首先我们要知道环境变量本质就是一些数据,环境变量可以类比命令行参数,我们当前的子进程环境变量其实是从父进程
(bash)来的,我们之前学习过,子进程会继承父进程的代码和数据。
有了前面的知识,我们可以做个总结:
父进程bash在命令行解析运行程序时会维护两张表:命令行参数表和环境变量表,命令行参数表一直在变,因为命令行一直在输入指令,而环境变量表相对比较稳定,父进程bash在fork子进程后子进程会继承父进程的代码和数据,子进程自然而然就可以看到并获取环境变量了。
看到这里我想大家一定还有问题,父进程bash是如何获取环境变量数据的呢?我们知道命令行参数数据是由用户输入的,父进程bash可以直接获取,那环境变量呢?小编这里直接揭晓答案了,后面用示例验证,其实bash是从系统的配置文件中获取的环境变量,系统中有一些配置文件会记录当前环境变量的名字和内容,当我们登陆Xshell时系统会创建并运行bash,bash就会用读文件操作读取这些配置文件中的内容,然后在bash进程内部动态申请指针数组(环境变量表),接着解析读取到的环境变量内容依次放入环境变量表中。
所以我们介绍的命令行参数表和环境变量表这两张表本质是内存级的,这里我们就能理解为什么前面我们把PATH清空后再重启Xshell后环境变量PATH又复原了,原因就是重启Xshell后系统重新读取配置文件把环境变量又加载到内存中了。
这些配置文件一般位于普通用户的家目录中,只不过它们是隐藏文件,所以要查看它们需要加-a,我们重点关注用方框框起来的两个文件:
在这里插入图片描述
我们来看看这两个文件中的内容,里面大部分都是shell脚本,大家看不懂没关系,简单来说父进程bash会执行这些脚本把对应的环境变量导到它的地址空间里或者说程序里。
在这里插入图片描述


在这里插入图片描述
验证:
下面我们来验证一下,我们修改.bash_profile文件中的PATH路径,把我们自己的路径添加进去,然后echo一句话:
在这里插入图片描述
我们退出Xshell然后重新登陆:
在这里插入图片描述
我们看到输出的一句话代表配置文件确实被执行了,被添加进PATH的路径里的可执行文件也可以像指令一样在任意位置运行了。
但是并不是所有环境变量都是从配置文件来,还有少部分是bash启动之后动态获取或创建的,比如PWD,它是从bash调用系统调用接口getcwd() 获取的,具体过程如下:当 bash 启动时,会调用 getcwd() 获取初始 cwd,并存入 PWD 环境变量。当用户执行 cd 命令切换目录时,bash 会先通过 chdir() 系统调用修改自身的 cwd,然后立即调用 getcwd() 获取新的 cwd 路径,更新到 PWD 环境变量中。
getcwd()文档:
在这里插入图片描述
getcwd()可以获取当前进程的工作路径,哪个进程调用getcwd()就返回哪个进程的当前工作路径,它的第一个参数是缓冲区,第二个参数是缓冲区的大小,返回值是当前工作路径字符串的首元素地址。
下面是一段示意代码,不仅是bash可以调用getcwd,子进程也可以通过getcwd获取子进程的pwd。
intmain(){char pwdbuf[128];char* pwd =getcwd(pwdbuf,sizeof(pwdbuf));printf("%s\n", pwd);return0;}
运行结果:
在这里插入图片描述

环境变量的作用

我们前面介绍了许多环境变量的周边概念,那环境变量在实际开发中有哪些作用呢?下面小编编写一个只有自己才能运行的程序,root来了都不行。
intmain(){char* who =getenv("USER");if(strcmp(who,"fdb")!=0){printf("我不让你执行:%s\n", who);return1;}//自己才能运行的程序:printf("这个程序只有我能执行,root也没招!\n");return0;}
运行结果:
在这里插入图片描述


在这里插入图片描述
借助这个例子小编想告诉大家不同的环境变量会有不同的用途,它是全局的,如果你想获取可以随时获取。系统在某些场景下会自动获取环境变量,比如(cd -) (cd ~) pwd等等。

本地变量和相关指令

在linux系统中,除了环境变量,还有本地变量的概念,本地变量只在父进程(bash)内部有效,它不会进环境变量表,不会像环境变量一样能继承给子进程。下面是几个相关的指令介绍:
echo:显⽰某个环境变量值
export:设置⼀个新的环境变量
在这里插入图片描述
env:显⽰所有环境变量
unset:清除环境变量
在这里插入图片描述
set:显⽰所有变量,包括本地变量和环境变量

环境变量的全局性

下面我们来聊聊环境变量为什么是全局变量,我们还是先看一段示例代码:
intmain(){char* myenv =getenv("MYENV");if(myenv ==NULL){printf("MYENV不存在!\n");}else{printf("MYENV=%s\n", myenv);}return0;}
在这里插入图片描述
上面的运行结果是 “MYENV不存在!:是因为我们创建的MYNEV是本地变量,.本地变量只有bash能拿到,/a.out创建的子进程拿不到这个本地变量。
在这里插入图片描述
当我们把MYENV导成环境变量后,子进程就能拿到这个环境变量了,同一份代码结果也发生了变化。
正因为bash的环境变量会把环境变量继承给子进程,而子进程也会创建子进程也可以继承到环境变量,所以以bash为发起点,它的子进程、孙子进程…都能继承到bash的环境变量,所以我们说环境变量是全局变量,具有全局属性。
在这里插入图片描述

内建命令的引出

我们先看下面的示例:
在这里插入图片描述
为什么这里的由bash创建的echo命令子进程能拿到本地变量MYENV并打印呢?这里小编需要引入一个概念:内建命令
在linux中,大部分命令是可执行程序,也就是user/bin路径下的可执行文件,需要通过bash创建的子进程执行,还有一部分命令因为执行的时候没有风险,需要bash进程自己执行,我们把这些命令称作内建命令。
(但是有些内建命令比较特殊,也会在user/bin路径下存在,但是它运行时不会创建子进程,这样做是为了在脚本模式中能执行echo命令。)

以上就是小编分享的全部内容了,如果觉得不错还请留下免费的关注和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~

在这里插入图片描述

Read more

GoWeb必备理论

GoWeb必备理论

关于goweb,你不得不知道的知识 若是初学者可以借鉴GoWeb查阅本文。 HTTP状态码: 意义 每个状态码都是,http设计者对“网络通讯”中可能出现的情况的假设、预判。他就相当于现实世界的信号灯,就像大家一遇到404,就知道资源找不到了。一遇到500就知道服务器挂了。这种共识,也就是如今万维网的高效率的基础之一。 http状态码是日常开发,修改bug,的居家必备神器。咱们对常见状态码做了分类。 1、必须掌握的状态码 200 ok 最常见的状态码,代表请求完全正确,比如打开网页、调用api啥的。 301 moved permanently 资源永久迁移(例:访问时a.com会被从定项到b.com) 302 Found (部分资源,临时迁移) 400 Bad request(请求出错,参数缺少什么的..) 401 unauthorized(没有登入) 403 forbidden(

By Ne0inhk
Flutter 三方库 tflite_web 端云协同 AI 引擎鸿蒙化高配适配:搭建异构计算 WebGL 后台管线并强力驱动 TensorFlow Lite-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 tflite_web 端云协同 AI 引擎鸿蒙化高配适配:搭建异构计算 WebGL 后台管线并强力驱动 TensorFlow Lite-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 tflite_web 端云协同 AI 引擎鸿蒙化高配适配:搭建异构计算 WebGL 后台管线并强力驱动 TensorFlow Lite 轻量大模型推理内核运转 前言 在 OpenHarmony 构建混合架构(Hybrid App)的过程中,将 AI 能力直接下沉到客户端侧执行已成为主流趋势。虽然鸿蒙原生提供了强大的 AI 框架,但对于已有大量积累、且运行在 Flutter Web 容器中的应用而言,寻找一致性的端侧 AI 推理方案至关重要。tflite_web 库为基于 Flutter Web 的应用提供了调用 TensorFlow Lite 模型的能力。本文将调研其在鸿蒙 Web

By Ne0inhk
《Web 自动化测试入门:从概念到百度搜索实战全拆解》

《Web 自动化测试入门:从概念到百度搜索实战全拆解》

一、自动化的核心概念 1. 定义:通过自动方式替代人工操作完成任务,生活中常见案例(自动洒水机、自动洗手液、超市闸机)体现了 “减少人力消耗、提升效率 / 质量” 的特点。 2. 软件自动化测试的核心目的: * 用于回归测试:软件迭代新版本时,验证新增功能是否影响历史功能的正常运行。 3. 常见面试题解析: * 自动化测试不能完全取代人工测试:需人工编写脚本,且功能变更后需维护更新,可靠性未必优于人工。 * 自动化测试不能 “大幅度降低工作量”:仅能 “一定程度” 减少重复工作,需注意表述的严谨性。 二、自动化测试的分类 自动化是统称,包含多种类型,核心分类及说明如下: 分类说明接口自动化针对软件接口的测试,目的是验证接口的功能、性能、稳定性等。UI 自动化 针对软件界面的测试,包含: 1. 移动端自动化:通过模拟器在电脑上编写脚本,测试手机应用;稳定性较差(受设备、

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 sanitize_html 彻底杜绝 XSS 注入风险(鸿蒙 Web 内容安全净化)

Flutter for OpenHarmony: Flutter 三方库 sanitize_html 彻底杜绝 XSS 注入风险(鸿蒙 Web 内容安全净化)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在开发 OpenHarmony 应用时,如果我们需要在 UI 中渲染来自后端的 HTML 内容(例如文章正文、用户评论),或者使用 flutter_html 等库,一个致命的安全风险就是 XSS (跨站脚本攻击)。恶意代码可能会通过 <script> 标签或 onerror 属性在你的 App 内执行非法逻辑。 sanitize_html 是一个轻量级且极高效的 HTML 净化库。它采用白名单机制,能瞬间过滤掉所有不安全的标签和属性,确保你在鸿蒙 App 内渲染的每一行 Web 内容都是绝对安全的。 一、核心防御机制解析 sanitize_html 遵循“默认拒绝”

By Ne0inhk