【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 : 导入环境变量

总结:

我们目前已经认识了很多的环境变量,但是其实使用的还是很少,而且它到底有什么用?我们会在下一节进程地址空间来更加具体的理解。加油吧,各位!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/979817.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计Python+DeepSeek-R1大模型游戏推荐系统 Steam游戏推荐系统 游戏可视化 游戏数据分析(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Python实现GO鹅优化算法优化BP神经网络回归模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 传统BP神经网络的局限性&#xff1a;BP&#xff08;Back Propagation&#xff09;神经网络作为一种…

1.忆往昔—Java发展史

在编程世界的远古时代&#xff0c;C语言和C统治着大地&#xff0c;但它们复杂且难以驾驭。1995年5月23日&#xff0c;Java 1.0正式发布&#xff0c;它像一把神奇的钥匙&#xff0c;打开了“一次编写&#xff0c;到处运行”的大门。 早在1991年Java就已经初见雏形&#xff0c;不…

Vue+Elementui 全局配置el-table表格列宽可拖拽

1、需求分析 如何让表格列宽可以拖动 elementui的el-table如果想要列宽可以拖动的话 有一个属性叫 border 在模板里添加这个属性即可实现 但是系统里面的表格我不可能一个一个去添加border太麻烦 如果能够全局配置岂不是非常省时间吗 我们在main.js里面通过全局混入的方式来…

“Web渗透测试实战指南|BWAPP靶场全关卡通关教程(含高中低/不可能级别)从SQL注入到XSS攻击手把手教学|网络安全工程师必备技能“ 内容较长点赞收藏哟

目录 Low级别 ---A1 - Injection{注入}-- HTML Injection - Reflected (GET) HTML Injection - Reflected (POST) HTML Injection - Reflected (URL) HTML Injection - Stored (Blog) iFrame Injection LDAP Connection Settings Mail Header Injection (SMTP) OS Co…

释放 Cursor 的全部潜能:快速生成智能 Cursor Rules

释放 Cursor 的全部潜能&#xff1a;使用 PromptCoder 从 package.json 快速生成智能 Cursor Rules 我们将深入探讨如何利用您项目中的 package.json 文件&#xff0c;轻松生成 Cursor Rules&#xff0c;并通过 PromptCoder 这个强大的工具&#xff0c;快速创建高质量的 curso…

DeepSeek开源周-汇总

当 ChatGPT、Claude 这些闭源大模型严防死守技术秘密时&#xff0c;DeepSeek 却反其道而行&#xff0c;选择了全面开源&#xff0c;为整个 AI 生态注入新的活力。 在过去短短一周内&#xff0c;DeepSeek 连续在 GitHub 开源了 8 个核心技术项目&#xff0c;完成了一次震撼业界…

02内存映射与bmp解码

一、mmap 内存映射 内存映射的作用是把硬件设备的地址&#xff0c;映射到应用层的内存空间&#xff0c;这样用户就可以跨越系统层访问linux的硬件设备。 1、man 2 mmap 查看映射函数接口 NAMEmmap, munmap - map or unmap files or devices into memory映射 解除…

I2C驱动(九) -- i2c_adapter控制器驱动框架编写

相关文章 I2C驱动(一) – I2C协议 I2C驱动(二) – SMBus协议 I2C驱动(三) – 驱动中的几个重要结构 I2C驱动(四) – I2C-Tools介绍 I2C驱动(五) – 通用驱动i2c-dev.c分析 I2C驱动(六) – I2C驱动程序模型 I2C驱动(七) – 编写I2C设备驱动之i2c_driver I2C驱动(八) – 编写I2C…

分布式系统核心基石:CAP定理、BASE理论与一致性算法深度解析

一、CAP定理&#xff1a;分布式系统的设计边界 1.1 核心定义与经典三角 CAP定理&#xff08;Brewers Theorem&#xff09;指出&#xff0c;在分布式系统中&#xff0c;一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&a…

3 算法1-4 过河卒

题目描述 棋盘上 A 点有一个过河卒&#xff0c;需要走到目标 B 点。卒行走的规则&#xff1a;可以向下、或者向右。同时在棋盘上 C 点有一个对方的马&#xff0c;该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。 棋盘用坐标表示&#xff…

AutoMQ:无需 Cruise Control 实现 Kafka 的自动分区再平衡

导读&#xff1a;AutoMQ是一款贯彻云优先理念来设计的 Kafka 替代产品。AutoMQ 创新地对 Apache Kafka 的存储层进行了基于云的重新设计&#xff0c;在 100% 兼容 Kafka 的基础上通过将持久性分离至 EBS 和 S3 带来了 10x 的成本降低以及 100x 的弹性能力提升&#xff0c;并且相…

论文阅读之基于Syn2Real域的侧扫声纳类水雷目标探测

摘要 由于现实世界数据的稀缺性&#xff0c;基于深度学习的水下水雷探测受到了限制。这种稀缺性导致过拟合&#xff0c;即模型在训练数据上表现良好&#xff0c;但在未见数据上表现不佳。本文提出了一种使用扩散模型的Syn2Real &#xff08;Synthetic to Real&#xff09;域泛…

如何使用Docker搭建哪吒监控面板程序

哪吒监控(Nezha Monitoring)是一款自托管、轻量级的服务器和网站监控及运维工具,旨在为用户提供实时性能监控、故障告警及自动化运维能力。 文档地址:https://nezha.wiki/ 本章教程,使用Docker方式安装哪吒监控面板,在此之前,你需要提前安装好Docker. 我当前使用的操作系…

微服务学习(1):RabbitMQ的安装与简单应用

目录 RabbitMQ是什么 为什么要使用RabbitMQ RabbitMQ的安装 RabbitMQ架构及其对应概念 队列的主要作用 交换机的主要作用 RabbitMQ的应用 通过控制面板操作&#xff08;实现收发消息&#xff09; RabbitMQ是什么 RabbitMQ是一个开源的消息队列软件&#xff08;消息代理…

综合实验处理表格

新建excel表格&#xff0c;输入信息&#xff0c;另存为csv文件。 利用notepad打开csv文件&#xff0c;可以观察格式 目标&#xff1a;通过编程处理文件&#xff0c;实现对数据的处理&#xff0c;成绩求和以及评价 对数据逐行处理&#xff0c;读一行&#xff0c;处理一行&#…

【leetcode hot 100 560】和为K的子数组

解法一&#xff1a;用左右指针寻找字串&#xff0c;如果和>k&#xff0c;则减少一个数&#xff08;left&#xff09;&#xff1b;如果和<k&#xff0c;则加上一个数&#xff08;right&#xff09;。 class Solution {public int subarraySum(int[] nums, int k) {int nu…

STM32CubeMx DRV8833驱动

一、DRV8833驱动原理 ​ STBY口接单片机的IO口&#xff0c;STBY置0电机全部停止&#xff0c;置1才能工作。STBY置1后通过AIN1、AIN2、BIN1、BIN2 来控制正反转。 AIN1AIN2电机状态00停止1speed反转speed1正转11停止 其中A端&#xff08;AIN1与AIN2&#xff09;只能控制AO1与…

Android 图片压缩详解

在 Android 开发中,图片压缩是一个重要的优化手段,旨在提升用户体验、减少网络传输量以及降低存储空间占用。以下是几种主流的图片压缩方法,结合原理、使用场景和优缺点进行详细解析。 效果演示 直接先给大家对比几种图片压缩的效果 质量压缩 质量压缩:根据传递进去的质…

JavaWeb后端基础(3)

原打算把Mysql操作数据库的一些知识写进去&#xff0c;但是感觉没必要&#xff0c;要是现在会的都是简单的增删改查&#xff0c;所以&#xff0c;这一篇&#xff0c;我直接从java操作数据库开始写&#xff0c;所以这一篇大致就是记一下JDBC、MyBatis、以及SpringBoot的配置文件…