深入了解Linux中的环境变量

news2024/9/8 11:14:22

在Linux系统中,环境变量(Environment Variables)是用于配置操作系统和应用程序运行环境的一种机制。它们储存在键值对中,可以控制程序的行为、路径查找和系统配置。本文将深入探讨环境变量的基本概念、常见类型、设置和管理方法,以及一些实用的技巧。

一、环境变量的基本概念

环境变量是在操作系统环境中定义的一些变量,用于存储信息,以便在系统中共享和使用。这些变量可以影响程序的行为和操作系统的功能。

  • 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
  • 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
  • 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

 首先在介绍环境变量前我们先介绍以下 “命令行参数”这一概念。

我们平常在Linux中输入命令时通常是这么使用的   ls -l    copy  test.c   /home/user

为什么后面可以跟后缀使用呢,这其实就跟我们的命令行参数有关

在C和C++编程中,命令行参数通过main函数的参数传递给程序。标准的main函数签名如下:

int main(int argc, char *argv[]) 

这个签名表明程序可以接受命令行参数,并通过argcargv这两个参数来访问这些输入。接下来,我们详细讲解这两个参数的作用和使用方法。

一、argcargv 的含义
  • argc(argument count):表示命令行参数的数量,包括程序名本身。
  • argv(argument vector):是一个指向字符数组的指针数组,每个元素指向一个命令行参数的字符串。

 

二、argcargv 的详细说明
1. argc 的作用

argc是一个整数,表示命令行参数的数量。例如,如果我们运行一个名为my_program的程序,并传递两个参数:

./my_program arg1 arg2

此时,argc的值为3,因为包括程序名在内总共有三个参数 

2. argv 的作用

argv是一个指向字符串数组的指针数组。每个元素是一个char*,指向一个命令行参数。假设上面的程序命令行如下

./my_program arg1 arg2

 那么,argv的内容如下:

  • argv[0]:指向字符串"./my_program"(程序名)。
  • argv[1]:指向字符串"arg1"(第一个参数)。
  • argv[2]:指向字符串"arg2"(第二个参数)。

argv[argc]是一个空指针(NULL),用于标记数组的结束。

三、实例讲解

下面是一个简单的C程序,它演示了如何使用argcargv来处理命令行参数:

#include <stdio.h>

int main(int argc, char *argv[]) {
    // 打印命令行参数的数量
    printf("Number of arguments: %d\n", argc);
    
    // 遍历并打印每一个参数
    for (int i = 0; i < argc; i++) {
        printf("Argument %d: %s\n", i, argv[i]);
    }
    
    return 0;
}

 假设我们编译并运行该程序,传递一些命令行参数

./my_program arg1 arg2 arg3

 输出将是:

Number of arguments: 4
Argument 0: ./my_program
Argument 1: arg1
Argument 2: arg2
Argument 3: arg3

 

./my_program arg1 arg2 arg3

所以以上命令本质就是 程序的路径 + 名称   后面跟 和该进程匹配的选项

默认是交给父进程bash处理的! 命令中启动的程序都会变成进程,其实都是bash的子进程

那么为什么要有命令行参数呢?

本质:命令行参数本质是交给我们程序的不同选项,用来定制不同的程序功能

 下面是一个简单的例子

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 打印帮助信息
void print_help(const char *program_name) {
    printf("Usage: %s <command> [options]\n", program_name);
    printf("Commands:\n");
    printf("  help              Show this help message\n");
    printf("  add <num1> <num2> Calculate the sum of num1 and num2\n");
    printf("  sub <num1> <num2> Calculate the difference of num1 and num2\n");
    printf("  mul <num1> <num2> Calculate the product of num1 and num2\n");
    printf("  div <num1> <num2> Calculate the quotient of num1 and num2\n");
}

// 计算两个数的和
void calculate_sum(int num1, int num2) {
    printf("Sum: %d\n", num1 + num2);
}

// 计算两个数的差
void calculate_difference(int num1, int num2) {
    printf("Difference: %d\n", num1 - num2);
}

// 计算两个数的积
void calculate_product(int num1, int num2) {
    printf("Product: %d\n", num1 * num2);
}

// 计算两个数的商
void calculate_quotient(int num1, int num2) {
    if (num2 == 0) {
        printf("Error: Division by zero is not allowed.\n");
        return;
    }
    printf("Quotient: %d\n", num1 / num2);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Error: No command provided.\n");
        print_help(argv[0]);
        return 1;
    }

    if (strcmp(argv[1], "help") == 0) {
        print_help(argv[0]);
    } else if (strcmp(argv[1], "add") == 0 && argc == 4) {
        int num1 = atoi(argv[2]);
        int num2 = atoi(argv[3]);
        calculate_sum(num1, num2);
    } else if (strcmp(argv[1], "sub") == 0 && argc == 4) {
        int num1 = atoi(argv[2]);
        int num2 = atoi(argv[3]);
        calculate_difference(num1, num2);
    } else if (strcmp(argv[1], "mul") == 0 && argc == 4) {
        int num1 = atoi(argv[2]);
        int num2 = atoi(argv[3]);
        calculate_product(num1, num2);
    } else if (strcmp(argv[1], "div") == 0 && argc == 4) {
        int num1 = atoi(argv[2]);
        int num2 = atoi(argv[3]);
        calculate_quotient(num1, num2);
    } else {
        fprintf(stderr, "Error: Invalid command or arguments.\n");
        print_help(argv[0]);
        return 1;
    }

    return 0;
}

编译并运行上述程序后,可以通过传递不同的命令行参数来执行不同的功能。例如:

./my_program sub 10 5

 那为什么内建命令不需要./呢,所以接下来我们继续讲解环境变量 

常见的环境变量包括:

  • PATH:存储可执行文件的目录路径列表,当你在终端输入命令时,系统会在这些目录中搜索可执行文件。
  • HOME:当前用户的主目录路径。
  • USER:当前登录的用户名。
  • SHELL:用户默认的Shell解释器。
  • LANG:系统的语言和区域设置。

二、查看环境变量

可以使用printenvenv和echo命令来查看当前的环境变量:

echo + $PATH/HOME 

 

 Linux中存在一些全局的设置,告诉命令行解释器应该去哪些路径下去寻找可执行程序

而系统中的很多配置,在我们刚刚登录到Linux中,就已经被加载到了bash进程(内存)中了,

所以我们默认查到的环境变量是内存级别的也就是说不会改变磁盘中的环境变量,当我们再次登录这个环境变量还是会重置!

所以最开始的环境变量不在内存中,而在对应的配置文件中

例如

  • .bash_profile
  • .bashrc
  • /etc/bashrc
和环境变量相关的命令
  • 1. echo: 显示某个环境变量值
  • 2. export: 设置一个新的环境变量
  • 3. env: 显示所有环境变量
  • 4. unset: 清除环境变量
  • 5. set: 显示本地定义的shell变量和环境变量

当执行当前目录下的外部命令时,需要使用 ./ 进行路径指定。这是因为:

  1. 安全性考虑:为了避免安全隐患,默认情况下,当前目录 . 并不包含在 PATH 环境变量中。这防止了恶意可执行文件以常用命令名命名并被误执行。
  2. 明确路径:通过使用 ./ 明确指定当前目录,用户告诉 Shell 要在当前目录中查找并执行相应的可执行文件。

例如,当前目录下有一个名为 my_script.sh 的脚本:

./my_script.sh

如果没有 ./,Shell 会按照 PATH 中指定的目录查找 my_script.sh,而不是当前目录。

所以我们把这个可执行程序的路径加入到环境变量中以后就不需要带有路径了。 

 

 三、环境变量的组织方式

环境变量在 Bash 内部是以一个键值对(key-value pair)的形式存储的。这些键值对存储在一个全局变量中,该变量在 Bash 的整个生命周期内都可以访问。 

环境变量的存储实际上依赖于操作系统提供的环境表。每个进程都有一个环境表,这个环境表是一个字符串数组,其中每个字符串的形式为 KEY=VALUE。在 Bash 中,使用外部命令 printenv 或内建命令 export 可以查看和管理这些变量。

也就是说bash进程启动的时候会默认给子进程形成两张表,一个是argv[]命令行参数表,另一个是environ[]环境变量表。bash通过各种方式传递给子进程

 

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

命令行第三个参数 

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

 通过第三方变量来获取

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

五、内建命令 

内建命令(built-in commands)是由 Shell 自身实现和提供的一类命令。与外部命令(external commands)不同,内建命令不依赖于文件系统中的独立可执行文件,而是直接在 Shell 进程中执行。内建命令的设计和实现旨在提高效率、提供更紧密的 Shell 集成以及实现一些只有 Shell 可以处理的特殊功能。下面详细介绍内建命令的作用与用途。

一、内建命令的主要作用
1. 提高执行效率

由于内建命令直接由 Shell 处理,不需要创建新的进程,因此执行速度比外部命令更快。每次执行内建命令时,Shell 不需要通过文件系统查找命令或创建子进程,这减少了开销和延迟。

2. 提供关键的 Shell 功能

内建命令实现了许多核心的 Shell 功能和控制结构,例如:

  • 环境管理exportsetunset 等命令用于设置和管理环境变量。
  • 目录操作cd 用于改变当前工作目录。
  • Shell 控制exit 退出 Shell 会话,exec 替换当前 Shell 进程。
  • 条件和循环ifforwhile 等控制结构实现了脚本中的条件和循环逻辑。
3. 处理 Shell 内部状态

一些内建命令可以直接操作和修改 Shell 的内部状态,例如设置选项、启用或禁用特性等:

  • 选项设置set 命令可以打开或关闭 Shell 的各种选项,例如调试模式、命令别名等。
  • 历史记录管理history 命令用于查看和操作命令历史记录。
4. 实现复杂的命令组合和脚本

内建命令在脚本编写中起着至关重要的作用。它们提供了丰富的编程构造,使得复杂的命令组合和脚本实现成为可能:

  • 函数定义function 用于定义 Shell 函数。
  • 输入输出重定向read 命令用于从标准输入读取数据。
二、内建命令的实现原理

内建命令是 Shell 内部实现的一部分,通常用 C 语言编写,直接集成在 Shell 的源代码中。当用户输入一个命令时,Shell 按照以下顺序进行处理:

  1. 检查内建命令:首先检查输入的命令是否为内建命令。如果是,则直接在当前进程中执行该命令。
  2. 检查别名:如果不是内建命令,Shell 接下来检查是否有定义的别名。
  3. 检查函数:然后,Shell 检查是否有定义的 Shell 函数。
  4. 搜索外部命令:最后,如果上述都不是,Shell 在 PATH 环境变量指定的目录中搜索外部命令。

这种查找顺序确保了内建命令的高效执行和优先处理。

四、总结

内建命令在 Shell 中发挥着重要作用。它们提高了命令执行的效率,提供了许多关键功能和控制结构,并且能够直接操作 Shell 的内部状态。了解和熟练使用内建命令,可以显著提升使用 Shell 和编写脚本的效率和灵活性。

如果你有任何进一步的问题或需要详细解释的内容,欢迎在评论区留言讨论。

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第十七届全国大学生信息安全竞赛创新实践能力赛初赛部分复现

Misc 神秘文件 1.根据提示信息&#xff0c;均需要从ppt中提取信息 2.在ppt的属性中发现一串密文和key&#xff0c;解密之后得到第一部分&#xff0c;根据提示Bifid chipher&#xff0c;为双歧密码解密&#xff0c;使用Bifid Cipher Decode解码 3.在第五张幻灯片&#xff0c;…

香橙派Kunpeng Pro测评:他给的实在太多了

文章目录 一、开箱环节1、包装配置2、开发板包装3、开发板3.1、开发版正面3.2、开发板背面 二、硬件配置1、硬件配置清单 2、配置图解 三、开机~启动&#xff01;1、运行系统1.1、外设配置1.2、系统启动1.3、官方教程 2、openEuler系统概览 四、系统测试1、性能测试1.1、安装sy…

现代 c++ 三:移动语义与右值引用

移动语义很简单&#xff0c;但它相关联的术语很复杂。本文尝试从历史的角度解释清楚这些乱七八糟的术语及其关联&#xff1a; 表达式 (expression)、类型&#xff08;type&#xff09;、值类别 (value categories)&#xff1b; 左值 (lvalue)、右值 (rvalue)、广义左值 (glval…

电脑找不到opencl.dll原因分析及5种详细的解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到opencl.dll”。这通常意味着计算机中缺少或损坏了与OpenCL&#xff08;开放计算语言&#xff09;相关的动态链接库文件。OpenCL允许应用程序利用图形处理器&#xff08;GPU&#xff…

[STM32-HAL库]ADC采集-DMA中断采集-平均值滤波-STM32CUBEMX开发-HAL库开发系列-主控STM32F103C8T6

目录 一、前言 二、实现步骤 1.STM32CUBEMX配置 2.Keil工程程序设计 三、结语 一、前言 本文通过STM32CUBEMX实现对ADC的数据采集和滤波操作&#xff0c;帮助各位开发者完成与模拟量输入的采集工作。 二、实现步骤 1.STM32CUBEMX配置 以STM32F103C8T6为例&#xff0c;打开S…

接口响应断言-json

json认识JSONPath源码类学习/json串的解析拓展学习 目的&#xff1a;数据返回值校验测试 json认识 json是什么-是一种数据交换格式&#xff0c;举例平时看到的json图2&#xff0c;在使用中查看不方便&#xff0c;会有格式转化的平台&#xff0c;json格式的展示 JSON在线视图…

OSPF减少LSA更新量1

OSPF的LSA优化 一、汇总——优化骨干区域 (1)域间汇总ABR设备基于某个区域的1/2类LSA计算所得的最佳路由&#xff0c;共享给其他区域时&#xff0c;进行汇总传递。 [r2]ospf 1 [r2-ospf-1]area 1——明细路由所在区域&#xff0c;该ABR设备必须和明细路由在同一区域 [r2-ospf…

学习javascript的函数

1.什么是函数&#xff1f; 可以重复被使用的代码块 作用&#xff1a;函数可以把具有相同或者相似逻辑的代码“包裹起来”&#xff0c;有利于代码的复用。 2.函数的基本使用 1.定义函数 利用关键字Function 定义函数&#xff08;声明函数&#xff09; function 函数名(){函…

windows-386、windows-amd64、windows-arm64这三者有什么区别?

选择文件的版本出现下面问题&#xff1a; Architectures windows-386 &#xff1a;这些是针对 32 位 Windows 系统编译的。windows-amd64 &#xff1a;这些是针对具有 AMD 或 Intel x86-64 架构的 64 位 Windows 系统编译的。windows-arm64 &#xff1a;这些是针对具有 ARM 架…

模型实战(20)之 yolov8分类模型训练自己的数据集

yolov8分类模型训练自己的数据集 yolov8,一个实时快速的端到端的集检测、分割、分类、姿态识别于一体的视觉算法库/框架本文将给出yolov8 分类模型的数据集制作格式及训练流程 1. 环境搭建 关于虚拟环境的搭建真的是老生常谈了,给出一个简单的搭建流程吧#新建虚拟环境 conda …

大模型时代的具身智能系列专题(三)

清华高阳团队 高阳为清华叉院助理教授&#xff0c;本科毕业于清华大学计算机系&#xff0c;博士毕业于UC Berkeley。博士导师是Vision领域的大牛Trevor Darrell&#xff0c;读博期间和Sergey Levine合作开始强化学习方面的探索&#xff0c;博后跟随Pieter Abbeel做强化学习&am…

7.类和对象

类和对象 当我们没有去了解过java的知识点中 不免产生一些问题&#xff1a; 什么是类&#xff1f;什么是对象&#xff1f; 记住一句话&#xff1a;在java当中 一切皆对象 类&#xff1a;是用来描述一个对象的 而对象是一个真正存在的实体 在Java这门纯面向对象的语言中 我们…

利用ArcGIS Python批量拼接遥感影像(arcpy batch processing)

本篇文章将说明如何利用ArcGIS 10.1自带的Python IDLE进行遥感影像的批量拼接与裁剪。 1.运行环境&#xff1a;ArcGIS10.1 (安装传送门)、Python IDLE 2.数据来源&#xff1a;地理空间数据云 GDEMV2 30M分辨率数字高程数据 3.解决问题&#xff1a;制作山西省的DEM影像 如下…

重生之 SpringBoot3 入门保姆级学习(05、 Bean 装配)

重生之 SpringBoot3 入门保姆级学习&#xff08;05、 Bean 装配&#xff09; [TOC](重生之 SpringBoot3 入门保姆级学习&#xff08;05、 Bean 装配&#xff09;)2.2.1 通过 ioc.xml 文件配置2.1.2 通过 Configuration 注解配置2.2.4 测试 Bean 是否生效 新建 User 类 package…

构建镜像时候出现奇怪的现象时候

一、背景 构建镜像时候&#xff0c;昨天还好好的&#xff0c;今天出现奇怪的现象 二、查看现象 docker system df#cache 显示600G 三、步骤 这操作比较轻微&#xff0c;20以前的缓存清理掉 docker builder prune --filter until480h # 清除20填以前的构建缓

深入理解栏目函数:从基础结构到实际应用

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、栏目函数的基本结构解析 代码案例 二、栏目函数的参数理解与返回值探究 参数取值与返…

决策树|随机森林 GBDT XGBoost|集成学习

文章目录 1 决策树模型1.1 决策树模型简介1.2 决策树模型核心问题1.2.1 分类划分标准1.2.1.1 信息增益1.2.1.2 增益率1.2.1.3 基尼系数 1.2.2 停止生长策略1.2.3 剪枝策略 1.3 决策树 - python代码1.3.1 结果解读1.3.2 决策树可视化1.3.3 CV - 留一法 2 集成学习2.1 Boosting2.…

MySQL:如果用left join的话,左边的表一定是驱动表吗

一、前言 在日常开发过程中关于MySQL的优化方面&#xff0c;我们知道小表驱动大表原理。例如left join&#xff0c;放在左边的表作为驱动表。但是用left join的话&#xff0c;左边的表一定是驱动表吗&#xff0c;本文将通过案例分析给出详细分析。 二、概念 在MySQL中&#xf…

初识C++ · 模拟实现vector

目录 前言&#xff1a; 1 部分简单函数的实现 2 push_back和pop_back 3 reserve和resize 4 Print_vector 5 insert和erase 6 拷贝构造 7 构造 8 赋值 9 memcpy的问题 10 迭代器失效 前言&#xff1a; 继上文模拟实现了string之后&#xff0c;接着就模拟实现vector&…

Java三种方法实现多线程,继承Thread类,实现Runnable接口,实现Callable接口

目录 线程&#xff1a; 继承Thread类&#xff1a; 实现Runnable类&#xff1a; 实现Callable接口&#xff1a; 验证多线程&#xff1a; 线程&#xff1a; 定义&#xff1a;进程可以同时执行多个任务&#xff0c;每个任务就是线程。举个例子&#xff1a;一个Java程序&#…