深入了解Linux中的环境变量

在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的程序,并传递两个参数:

cpp 复制代码
./my_program arg1 arg2

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

2. argv 的作用

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

cpp 复制代码
./my_program arg1 arg2

那么,argv的内容如下:

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

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

三、实例讲解

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

cpp 复制代码
#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;
}

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

cpp 复制代码
./my_program arg1 arg2 arg3

输出将是:

cpp 复制代码
Number of arguments: 4
Argument 0: ./my_program
Argument 1: arg1
Argument 2: arg2
Argument 3: arg3
cpp 复制代码
./my_program arg1 arg2 arg3

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

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

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

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

下面是一个简单的例子

cpp 复制代码
#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;
}

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

cpp 复制代码
./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 的脚本:

cpp 复制代码
./my_script.sh

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

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

三、环境变量的组织方式

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

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

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

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

命令行第三个参数

cpp 复制代码
#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;
}

通过第三方变量来获取

cpp 复制代码
#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 和编写脚本的效率和灵活性。

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

相关推荐
漫谈网络7 分钟前
基于 Netmiko 的网络设备自动化操作
运维·自动化·netdevops·netmiko
꧁坚持很酷꧂33 分钟前
Linux Ubuntu18.04下安装Qt Craeator 5.12.9(图文详解)
linux·运维·qt
凉、介1 小时前
PCI 总线学习笔记(五)
android·linux·笔记·学习·pcie·pci
电鱼智能的电小鱼1 小时前
EFISH-SBC-RK3588无人机地面基准站项目
linux·网络·嵌入式硬件·机器人·无人机·边缘计算
电鱼智能的电小鱼1 小时前
基于 EFISH-SBC-RK3588 的无人机环境感知与数据采集方案
linux·网络·嵌入式硬件·数码相机·无人机·边缘计算
小诸葛的博客2 小时前
详解Linux中的定时任务管理工具crond
linux·运维·chrome
一默19912 小时前
CentOS 7.9升级OpenSSH到9.9p2
linux·运维·centos
keep intensify3 小时前
Linux常用指令
linux·服务器·php
带电的小王3 小时前
sherpa-ncnn:Linux(x86/ARM32/ARM64)构建sherpa-ncnn --语音转文本大模型
linux·语音识别·实时音视频·sherpa-ncnn
沧浪之水!3 小时前
【Linux网络】:套接字之UDP
linux·网络·udp