【Linux】命令行参数 | 环境变量(四)
目录
前言:
一、命令行参数:
1.main函数参数
2.为什么有它?
二、环境变量:
1.main函数第三个参数
2.查看shell本身环境变量
3.PATH环境变量
4.修改PATH环境变量配置文件
5.HOME环境变量
6.SHELL环境变量
7.PWD环境变量
8.USER和LOGNAME环境变量
9.OLDPWD环境变量
三、理解环境变量:
1.本地变量(临时变量)
2.set查看所有变量
3.export导入环境变量
4.unset取消环境变量
5.环境变量的全局属性
6.环境变量表
四、命令总结:
总结:
前言:
我们已经了解了进程的很多概念,上次讲到了进程调度算法,这次我们来一个更炸裂的环境变量,大家应该都学过JAVA,每次都要下载并配置环境变量,这次,我们来彻底搞懂它。
一、命令行参数:
1.main函数参数
我们平时写C语言,main函数有参数吗?其实main函数有参数,但是我们从来不会写,这次我们把环境变量参数都打印出来并看看都是什么:

#include<stdio.h>int main(int argc, char *argv[])
{printf("argc: %d\n", argc);for (int i = 0; i < argc; ++i) {printf("argv[%d]: %s\n", i, argv[i]);}return 0;
}
等等,这里你可能无法直接编译生成可执行程序:
我们平时使用的VS后面其实默认添加了,所以我们再makefile中指定标准:
此时再次make就不会报错。
我们在命令行中多传入几个参数并观察结果:

当然也可以是其他字符:

所以,我们把main函数参数中的argv叫做命令行参数列表;argc叫做参数的个数。
2.为什么有它?
比如现在写一个只要两个参数的程序:
#include<stdio.h>
#include<string.h>// code -opt1/-opt2/-opt3
int main(int argc, char *argv[])
{if (argc != 2) {printf("Usage: code -opt\n");return 1;}if (strcmp(argv[1], "-opt1") == 0){printf("功能1\n");}else if (strcmp(argv[1], "-opt2") == 0){printf("功能2\n");}else if (strcmp(argv[1], "-opt3") == 0){printf("功能3\n");}else {printf("默认功能\n");}return 0;
}
我们来测试一下:

有没有似曾相识?我们平时使用的命令都会加上选项,而这些命令都是C语言实现的。可以让同一个程序,根据命令行参数的选项,表现出不同的功能。比如:指令中选项的实现。
在命令行中,我们平时输入的命令其实是一串字符串,首先会被shell(bash命令行)拿到,之后按照空格打散,形成一张表(argv)和元素个数(argc)。

命令行启动的程序,父进程都是shell。对于数据(尤其是只读的),子进程也能看到。
main函数也是被调用的,父进程都是shell,一般传入下面三个参数:
二、环境变量:
1.main函数第三个参数
我们刚才看到main函数的参数有三个,main函数第三个参数env,我们这次打印出所有的环境变量:


以key-value方式构建的,具有“全局”属性的变量,叫做全局属性。
常见的环境变量:
PATH: 指定命令的搜索路径
HOME: 指定用户的主工作目录(即用户登录到Linux系统中时,默认的目录)
SHELL: 当前Shell,它的值通常是/bin/bash
2.查看shell本身环境变量
在命令行想看shell本身自己的环境变量,输入env即可:

和之前打印出来的一样。
这次我们修改代码:


3.PATH环境变量
为什么系统知道命令在usr/bin目录下?我们可以让他认识我们的路径吗?
PATH环境变量,告诉了shell,执行命令时,应该去哪个路径下查。如果我们想指定看一个环境变量,可以使用如下命令:

这代表当shell运行任何一个命令时,首先要查PATH中的路径,之后看里面有没有对应的命令。
PATH是一个路径集合,是系统可执行文件的搜索路径的集合。
所以,我们可以修改PATH,把自己的路径添加到PATH中,就可以直接执行自己的命令了。

至于为什么有的命令还能跑,我们以后再解释。但是现在有一个问题,我们把之前的路径都覆盖了,怎么恢复?其实在环境变量加载的时候,我们就已经把其加载到了bash进程内部,是内存级的,保存在进程的上下文中。也就是相当于我们malloc出来一块空间,此时把malloc空间中的内容修改了,所以我们重启一下Xshell即可。

所以在修改PATH时,不要直接把PATH修改为我们想修改的路径,要把之前的路径加上去,再添加我们的路径:

所以环境变量PATH本质就是内存级的变量通过shell维护,可以通过一定方式修改PATH变量。所以这次我们关掉Xshell依旧还是原来的PATH路径,不会添加你之前添加的内容。
4.修改PATH环境变量配置文件
但是,这些内容一开始是从哪里的来的?最开始PATH环境变量一定不再内存中,而是在系统的配置文件中。
当我登陆的时候,会启动一个shell进程,此时就会读取用户和系统相关的环境变量的配置文件形成自己的环境变量表。
所以我们修改配置文件,我们一旦登录Linux一定是一个具体的用户在登陆,登录后一定处在自己的家目录下,所以系统中家目录下会存在两个配置文件(.bash_profile, .bashrc)。当bash启动时,就会读取这两个环境变量,形成自己的环境变量信息。

所以我们将之前code所在文件路径添加到.bash_profile文件中,并观察效果:

如果没有成功(因为刚才已经添加过了临时新的环境变量,所以这里没有执行该命令),让一个更改后的配置文件生效可以使用该命令:
source .bash_profile
我们之前讲解过uid,也就是进程内部会记录是谁启动的这个进程。但是你启动进程的时候,系统怎么会知道你是谁?并且把你的uid写入到pcb中呢?因为:

5.HOME环境变量

因为命令行执行的命令都是bash的子进程,所以当我们切换路径的时候,也就把bash的cwd的属性给改了。 所以创建的所有进程路径都源自于bash的cwd路径:

当我们切换路径,就会改变bash的cwd:

所以为什么最开始我们处于家目录中?因为最开始读取配置文件中的环境变量(HOME),然后bash把自己的cwd设置在了HOME变量中。
所以系统读取配置文件时,首先一定知道登录的用户是谁,一旦发现不是root,之后就会更改为/home/XXX把环境变量设置好,之后chdir更改cwd即可。把bash的cwd改为当前工作路径(家目录下)。
6.SHELL环境变量
还有一个环境变量是SHELL,它会记录系统启动时,使用的哪个shell(当前就是bash):

7.PWD环境变量
还有一个环境变量是PWD,是保存当前路径的:

为什么要这么做?
我们可以通过代码获取环境变量,如果按照之前的方法,是获取所有环境变量之后匹配,这样并不优雅,系统提供了对应的函数getenv:

getenv函数获取环境变量的内容,返回char*。获取成功返回值,失败返回NULL。

PWD是一个具体的命令,用于显示当前目录路径。
CWD 是一个概念,表示程序或进程当前的工作目录。
PWD的输出取决于进程的 CWD,而 CWD 是进程可以动态修改的。
8.USER和LOGNAME环境变量
我们直接打印出环境变量观察这两者的内容:

当前两者是一致的。此时执行su -:

我们再先以gan身份登录,之后登录到跟用户:

所以当我们登陆的时候,LOGNAME和USER是一致的;su -执行后本质是以root身份登录的;而su root不会改变原来的LOGNAME和USER,只是改变了执行身份。
所以我们区分使用系统的用户以环境变量USER区分。
我们可以修改envtest.c代码来观察现象:

之后先以gan用户执行,之后su -再次执行该代码:

也就是说我们可以通过代码来进行身份认证,我们这里可以演示一下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>int main()
{//可以让我的程序识别用户身份,只让gan用户访问const char* who = getenv("USER");if (strcmp(who, "gan") == 0) {printf("执行程序正常命令\n");return 0;} else{printf("无权访问!\n");return 1;}return 0;
}

这样就可以写一些可以对用户权限进行控制的程序。
9.OLDPWD环境变量
我们先记录当前路径:
![]()
之后cd ~回家,查看env。发现有一个OLDPWD环境变量记录我们上次所在路径:

再次cd -:

所以PWD记录当前工作路径,OLDPWD记录你上次所在工作路径。所以cd -就是基于这个环境变量实现的。
以上为认识环境变量。
三、理解环境变量:
系统提供的具有"全局"属性的变量。
1.本地变量(临时变量)
shell也支持我们直接在本地定义变量:

这样定义的变量不属于环境变量,我们使用env并查不到。
在命令行输入的"a=10"这样的语句其实是一段字符串,被shell先读到,shell(一个进程)也就会把这个字符串维护起来,就相当于malloc一块空间。这种变量叫做本地变量不会被环境变量查到。
2.set查看所有变量
如果现在想查到环境变量和本地变量都查到,可以使用set来查询:

本地变量一般给自己用。
3.export导入环境变量
我们也可以把本地变量导出到环境变量:

以下这个图方便各位更好地理解环境变量,本地变量和argv表:

当我们export i之后,会导入环境变量。

bash重启以后,export的变量也会消失。
当然也可以直接使用export b=100直接导入环境变量。
所以bash不仅认识变量,还认识while循环等语句,所以衍生出了一门shell脚本语言。
设计一个只执行一次的程序:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h>int main() {char* isrunning = getenv("ISRUNNING");if (isrunning == NULL){while(1){printf("当前进程首次启动!\n");sleep(1);}} else {printf("当前进程已经运行了!\n");}return 0; }
环境变量是可以被子进程继承的。
4.unset取消环境变量
我们使用unset取消导入的环境变量(unset不仅可以取消你自己定义的环境变量,还可以取消大多数非只读的环境变量和 shell 变量,包括一些由系统或 shell 自动设置的环境变量。不过,只读变量(如PWD等)无法被取消):

5.环境变量的全局属性
环境变量可以被所有bash之后的进程全部看到,所以环境变量具有"全局属性"。系统的配置信息,尤其是具有"指导性"的配置信息,它是系统配置起效的一种表现。
进程具有独立性,环境变量可以用来进程间传递数据(只读数据)。
6.环境变量表
其实还有第三种获取环境变量的方法:

使用environ获取环境变量, environ是一个包含在unistd.h中的全局变量,是一个二级指针,它指向环境变量的表:

我们可以用代码来使用一下它:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>extern char** environ;int main()
{for (int i = 0; environ[i]; ++i){printf("%s\n", environ[i]);}return 0;
}

四、命令总结:
env : 查看shell本身自己的环境变量。
set : 查看所有变量,包括临时变量。
unset : 取消非只读环境变量。
export : 导入环境变量
总结:
我们目前已经认识了很多的环境变量,但是其实使用的还是很少,而且它到底有什么用?我们会在下一节进程地址空间来更加具体的理解。加油吧,各位!
相关文章:
【Linux】命令行参数 | 环境变量(四)
目录 前言: 一、命令行参数: 1.main函数参数 2.为什么有它? 二、环境变量: 1.main函数第三个参数 2.查看shell本身环境变量 3.PATH环境变量 4.修改PATH环境变量配置文件 5.HOME环境变量 6.SHELL环境变量 7.PWD环境变…...
算法002——复写零
力扣——复写零点击即可跳转 这道题还是运用 双指针,我们从左往右开始,让 cur 0,dest 0,当我们循环时,会覆盖后面的值,所以从左到右无法实现,我们运用 从右到左的方式。 以示例一数组为例,从…...
例子 DQN + CartPole: 深入思考一下,强化学习确实是一场智能冒险之旅!
强化学习的概念 在技术人员眼里,深度学习、强化学习,或者是大模型,都只是一些算法。无论是简单,还是复杂,我们都是平静的看待。当商业元素日益渗透进技术领域,人人言必称大模型的时候。技术人该反思一下&a…...
java 实现xxl-job定时任务自动注册到调度中心
xxl-job 自动注册(执行器和任务) 前言 xxl-job是一个功能强大、简单易用、高可用且可扩展性强的分布式定时任务框架/分布式任务调度平台。它适用于各种需要定时任务调度的场景,并可根据业务需求进行灵活配置和扩展。 xxl-job简介 xxl-job是一个开源的分布式定时任务框架,…...
esp32串口通信
1、线路图 2、打开电脑的串口终端 3、eps32通过串口往电脑的串口终端输出信息: from machine import UART, Pin import time# 初始化UART0,波特率设置为115200 uart UART(0, baudrate115200, tx1, rx3)# 主循环 while True:# 要发送的消息#某些串口终…...
蓝桥杯备赛-前缀和-可获得的最小取值
问题描述 妮妮学姐手头有一个长度为 nn 的数组 aa,她想进行 kk 次操作来取出数组中的元素。每次操作必须选择以下两种操作之一: 取出数组中的最大元素。取出数组中的最小元素和次小元素。 妮妮学姐希望在进行完 kk 次操作后,取出的数的和最…...
UniApp 中封装 HTTP 请求与 Token 管理(附Demo)
目录 1. 基本知识2. Demo3. 拓展 1. 基本知识 从实战代码中学习,上述实战代码来源:芋道源码/yudao-mall-uniapp 该代码中,通过自定义 request 函数对 HTTP 请求进行了统一管理,并且结合了 Token 认证机制 请求封装原理ÿ…...
边缘计算+多模态感知:户外监控核心技术解析与工程部署实践!户外摄像头监控哪种好?户外摄像头监控十大品牌!格行视精灵VS海康威视VS大华横评!
一、核心参数解析与选型逻辑 1.环境适应性设计 极端天气防护:优先选择IP66/67防护等级的设备,例如格行视精灵通过IP67防水防尘设计可应对暴雨、沙尘暴等复杂环境,其密封轴承结构可有效防止水汽侵蚀内部电路。 温度耐受范围:北方…...
Spring项目-抽奖系统(实操项目)(ONE)
^__^ (oo)\______ (__)\ )\/\ ||----w | || || 一:前言: 随着互联网技术的快速发展,线上营销活动已成为企业吸引用户、…...
STM32-智能小车项目
项目框图 ST-link接线 实物图: 正面: 反面: 相关内容 使用L9110S电机模块 电机驱动模块L9110S详解 | 良许嵌入式 测速模块 语音模块SU-03T 网站:智能公元/AI产品零代码平台 一、让小车动起来 新建文件夹智能小车项目 在里面…...
Python:字符串常见操作
find(子字符串,开始位置下标,结束位置下标) 注意:开始位置和结束位置下标可以省略,表示在整个字符串中查找 stasdfghjkl print(st.find(a))#输出结果为0,表明a在第一个位置默认从零开始,找不到则返回-1 …...
Redis 哈希(Hash)
Redis 哈希(Hash) 概述 Redis 哈希(Hash)是一种特殊的键值对类型,它允许存储结构化的数据,例如一个对象或记录。每个哈希值可以包含多个字段,每个字段又可以存储一个字符串值。这使得Redis哈希非常适合用于存储对象的…...
Windows对比MacOS
Windows对比MacOS 文章目录 Windows对比MacOS1-环境变量1-Windows添加环境变量示例步骤 1:打开环境变量设置窗口步骤 2:添加系统环境变量 2-Mac 系统添加环境变量示例步骤 1:打开终端步骤 2:编辑环境变量配置文件步骤 3࿱…...
react 路由跳转的几种方式
在 React 中,路由跳转通常通过 react-router-dom(或类似的路由库)来实现。以下是几种常见的路由跳转方式: 1. 使用 <Link> 组件 <Link> 是最简单的路由跳转方式,它会生成一个 <a> 标签,…...
2.你有什么绝活儿?—Java能做什么?
1、Java的绝活儿:要问Java有什么绝活,我觉得它应该算是一位魔法师,会的绝活儿有很多,要说最能拿得出手的当属以下三个。 1.1 平台无关性:Java可以在任何地方施展魔法,无论是Windows、Linux还是Mac…...
2025年2月文章一览
2025年2月编程人总共更新了17篇文章: 1.2025年1月文章一览 2.《Operating System Concepts》阅读笔记:p2-p8 3.《Operating System Concepts》阅读笔记:p9-p12 4.《Operating System Concepts》阅读笔记:p13-p16 5.《Operati…...
C++ | 面向对象 | 类
👻类 👾语法格式 class className{Access specifiers: // 访问权限DataType variable; // 变量returnType functions() { } // 方法 };👾访问权限 class className {public:// 公有成员protected:// 受保护成员private:// 私有成员 }…...
leetcode:2164. 对奇偶下标分别排序(python3解法)
难度:简单 给你一个下标从 0 开始的整数数组 nums 。根据下述规则重排 nums 中的值: 按 非递增 顺序排列 nums 奇数下标 上的所有值。 举个例子,如果排序前 nums [4,1,2,3] ,对奇数下标的值排序后变为 [4,3,2,1] 。奇数下标 1 和…...
Visionpro cogToolBlockEditV2.Refresh()
在 C# 中使用 cogToolBlockEditV2.Refresh() 方法主要用于刷新 CogToolBlockEditV2 控件的显示状态,适用于动态更新界面或重新加载工具块(ToolBlock)的场景。以下是具体说明和典型应用场景。 基本作用 刷新控件显示:当修改了与 C…...
Apache Spark中的依赖关系与任务调度机制解析
Apache Spark中的依赖关系与任务调度机制解析 在Spark的分布式计算框架中,RDD(弹性分布式数据集)的依赖关系是理解任务调度、性能优化及容错机制的关键。宽依赖(Wide Dependency)与窄依赖(Narrow Dependency)作为两种核心依赖类型,直接影响Stage划分、Shuffle操作及容…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...




