【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环境变…...
java高级(IO流多线程)
file 递归 字符集 编码 乱码gbk,a我m,utf-8 缓冲流 冒泡排序 //冒泡排序 public static void bubbleSort(int[] arr) {int n arr.length;for (int i 0; i < n - 1; i) { // 外层循环控制排序轮数for (int j 0; j < n -i - 1; j) { // 内层循环…...
深度剖析数据分析职业成长阶梯
一、数据分析岗位剖析 目前,数据分析领域主要有以下几类岗位:业务数据分析师、商业数据分析师、数据运营、数据产品经理、数据工程师、数据科学家等,按照工作侧重点不同,本文将上述岗位分为偏业务和偏技术两大类,并对…...
【PHP脚本语言详解】为什么直接访问PHP文件会显示空白?从错误示例到正确执行!
前言 作为一名开发者,你是否曾经遇到过这样的问题:写了一个PHP脚本,放到服务器根目录后,直接通过file:///路径访问却显示空白页面?而换成http://localhost却能正常显示?这篇文章将带你深入理解PHP脚本语言…...
vue3 + xlsx 实现导出表格,动态获取表头和数据
针对第三方表格组件(如 vxe-table 或 el-table),通过其提供的 API 获取表头和数据,而不是直接操作 DOM。以下是针对 vxe-table 和 el-table 的通用导出函数封装: npm install xlsx1. 封装通用导出函数 import * as X…...
Web3.py 入门笔记
Web3.py 学习笔记 📚 1. Web3.py 简介 🌟 Web3.py 是一个 Python 库,用于与以太坊区块链进行交互。它就像是连接 Python 程序和以太坊网络的桥梁。 官方文档 1.1 主要功能 查询区块链数据(余额、交易等)发送交易与…...
NFC拉起微信小程序申请URL scheme 汇总
NFC拉起微信小程序,需要在微信小程序开发里边申请 URL scheme ,审核通过后才可以使用NFC标签碰一碰拉起微信小程序 有不少人被难住了,从微信小程序开发社区汇总了以下信息,供大家参考 第一,NFC标签打开小程序 https://…...
《Python实战进阶》No 8:部署 Flask/Django 应用到云平台(以Aliyun为例)
第8集:部署 Flask/Django 应用到云平台(以Aliyun为例) 2025年3月1日更新 增加了 Ubuntu服务器安装Python详细教程链接。 引言 在现代 Web 开发中,开发一个功能强大的应用只是第一步。为了让用户能够访问你的应用,你需…...
量子计算如何提升机器学习效率:从理论到实践
量子计算如何提升机器学习效率:从理论到实践 在人工智能和机器学习的高速发展中,传统计算方法已经逐渐面临性能瓶颈。随着数据量的激增、算法复杂度的提高,传统计算机在处理某些特定任务时的效率显得捉襟见肘。而量子计算,作为一…...
文档识别-C#中英文文档识别接口-PDF文件内容识别API
文档识别接口可满足用户在数字化转型过程中对文档处理的高效、准确需求。翔云文档识别接口以成熟的文字识别技术、自然语言处理技术、图像识别技术为核心,能够将文档上的非可编辑文本转化为可编辑的数据,从而提升信息处理的速度与实现文档数字化管理的准…...
【JAVA SE基础】抽象类和接口
目录 一、前言 二、抽象类 2.1 抽象类的概念 2.2 抽象类语法 2.3 抽象类特性 2.4 抽象类的作用 三、接口 3.1 什么是接口 3.2 语法规则 3.3 接口使用 3.4 接口特性 3.5 实现多接口 3.6 接口间的继承 四、Object类 4.1 获取对象信息( toString() &…...
530 Login fail. A secure connection is requiered(such as ssl)-java发送QQ邮箱(简单配置)
由于cs的csdN许多文章关于这方面的都是vip文章,而本文是免费的,希望广大网友觉得有帮助的可以多点赞和关注! QQ邮箱授权码到这里去开启 授权码是16位的字母,填入下面的mail.setting里面的pass里面 # 邮件服务器的SMTP地址 host…...
LeetCode第57题_插入区间
LeetCode 第57题:插入区间 题目描述 给你一个 无重叠的 ,按照区间起始端点排序的区间列表。在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。 难度 中…...
计算机毕业设计SpringBoot+Vue.js体育馆使用预约平台(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
LeetCode 热题 100_寻找两个正序数组的中位数(68_4_困难_C++)(二分查找)(先合并再挑选中位数;划分数组(二分查找))
LeetCode 热题 100_寻找两个正序数组的中位数(68_4) 题目描述:输入输出样例:题解:解题思路:思路一(先合并再挑选中位数):思路二(划分数组(二分查找…...
MyBatis-Plus 为简化开发而生【核心功能】
文章目录 一、前言二、快速入门1. 入门案例2. 常见注解3. 常见配置 三、核心功能1. 条件构造器2. 自定义 SQL3. Service 接口3.1 基本使用3.2 复杂条件 一、前言 顾名思义,MyBatis-Plus 其实是 MyBatis 的一个加强版,它可以帮助我们快速高效地编写数据库…...
【MySQL】(2) 库的操作
SQL 关键字,大小写不敏感。 一、查询数据库 show databases; 注意加分号,才算一句结束。 二、创建数据库 {} 表示必选项,[] 表示可选项,| 表示任选其一。 示例:建议加上 if not exists 选项。 三、字符集编码和排序…...
通信原理速成笔记(信息论及编码)
信息论基础 信息的定义与度量 信息是用来消除不确定性的内容。例如,在猜硬币正反的情境中,结果存在正反两种不确定性,而得知正确结果能消除这种不确定性,此结果即为信息。单个事件的信息量:对于离散信源中的事件xi&…...
云和恩墨亮相PolarDB开发者大会,与阿里云深化数据库服务合作
2025年2月26日,备受瞩目的阿里云PolarDB开发者大会于北京嘉瑞文化中心盛大举行,众多行业精英齐聚一堂,共襄技术盛会。云和恩墨作为阿里云重要的生态合作伙伴受邀参会。云和恩墨联合创始人兼技术研究院总经理杨廷琨与阿里云智能数据库产品事业…...
kafka consumer 手动 ack
在消费 Kafka 消息时,手动确认(acknowledge)消息的消费,可以通过使用 KafkaConsumer 类中的 commitSync() 或 commitAsync() 方法来实现。这些方法将提交当前偏移量,确保在消费者崩溃时不会重新消费已处理的消息。 以…...
final 关键字在不同上下文中的用法及其名称
1. final 变量 名称:final 变量(常量)。 作用:一旦赋值后,值不能被修改。 分类: final 实例变量:必须在声明时或构造函数中初始化。 final 静态变量:必须在声明时或静态代码块中初…...
PHP面试题--后端部分
本文章持续更新内容 之前没来得及整理时间问题导致每次都得找和重新背 这次整理下也方便各位小伙伴一起更轻松的一起踏入编程之路 欢迎各位关注博主不定期更新各种高质量内容适合小白及其初级水平同学一起学习 一起成为大佬 数组函数有那些 ps:本题挑难的背因为…...
Python 高精度计算利器:decimal 模块详解
Python 高精度计算利器:decimal 模块详解 在 Python 编程中,处理浮点数时,标准的 float 类型往往会因二进制表示的特性而产生精度问题。decimal 模块应运而生,它提供了十进制浮点运算功能,能让开发者在需要高精度计算…...
hbase相关问题处理
1.如果遇到ZK宕机,通过HTable和Connection两种连接方式获取数据,在实现原理和故障恢复上有何异同? 通过new HTable方式,则每次方法调用都会建立新的连接,而且会从zk获取表的元数据,会导致将业务的并发传导到zookeeper服务,会对全局所有依赖zookeeper服务的节点存在一定…...
Linux下的网络通信编程
在不同主机之间,进行进程间的通信。 1解决主机之间硬件的互通 2.解决主机之间软件的互通. 3.IP地址:来区分不同的主机(软件地址) 4.MAC地址:硬件地址 5.端口号:区分同一主机上的不同应用进程 网络协议…...
什么是“零日漏洞”(Zero-Day Vulnerability)?为何这类攻击被视为高风险威胁?
正文 零日漏洞(Zero-Day Vulnerability) 是指软件、硬件或系统中存在的、尚未被开发者发现或修复的安全漏洞。攻击者在开发者意识到漏洞存在之前(即“零日”内)利用该漏洞发起攻击,因此得名。这类漏洞的“零日”特性使…...
AI数据分析:用DeepSeek做数据清洗
在当今数据驱动的时代,数据分析已成为企业和个人决策的重要工具。随着人工智能技术的快速发展,AI 驱动的数据分析工具正在改变我们处理和分析数据的方式。本文将着重介绍如何使用 DeepSeek 进行数据清洗。 数据清洗是数据分析的基础,其目的是…...
把GB型材库放入solidwork中点击库无法应
1、文件夹的位置要选择对,如下图: 2、文件夹一定要嵌套三层,如下图...
【前端】XML,XPATH,与HTML的关系
XML与HTML关系 XML(可扩展标记语言)和 HTML(超文本标记语言)是两种常见的标记语言,但它们有不同的目的和用途。它们都使用类似的标记结构(标签),但在设计上存在一些关键的差异。 XML…...
IP-----动态路由OSPF(2)
这只是IP的其中一块内容,IP还有更多内容可以查看IP专栏,前一章内容为动态路由OSPF ,可通过以下路径查看IP-----动态路由OSPF-CSDN博客,欢迎指正 注意!!!本部分内容较多所以分成了两部分在上一章 5.动态路…...




