很多刚接触 Linux C 开发的同学,都会遇到两个 “拦路虎”:
为什么 main 函数要写 int argc, char *argv[ ]echo $PATH 里的路径到底是怎么让命令跑起来的?


其实,命令行参数和环境变量,就是程序和操作系统沟通的两大核心通道:

一个负责接收用户单次运行时的指令,

一个承载着系统全局的运行配置。

这篇文章就带你从 bash 解析命令行的底层过程讲起,把 argv 数组、environ 全局变量、环境变量的继承机制这些知识点一次性讲透,让你彻底搞懂程序和系统的 “对话逻辑”。

1.什么是环境变量

一句话总结:环境变量就是操作系统里的「全局配置参数」,程序和命令可以直接读取使用,用来知道 “系统在哪里、怎么运行、默认配置是什么”


你可以把它理解成:


系统给所有程序共享的 “小纸条”,上面写着路径、配置、用户名、临时目录等信息。

1. 最直观的例子:PATH 环境变量

ls
gcc
gdb
cgdb
python

为什么系统知道去哪里找这些命令?


因为它们的路径被写进了 PATH 环境变量 里
系统会按 PATH 里记录的路径挨个找,找到就运行。

2.环境变量长什么样?


1.格式:变量名 = 值

PATH=/usr/bin:/bin:/usr/local/bin
HOME=/home/user
LANG=en_US.UTF-8
PWD=/home/user/project

3. 环境变量从哪里来?


当你登录 Linux 系统,打开终端时,bash 会自动加载一系列配置文件,环境变量就是在这里被初始化的:


/etc/profile:系统级配置,对所有用户生效
~/.bashrc:用户级配置,只对当前用户生效
~/.bash_profile:用户登录时执行的配置文件


你可以用ls -la查看家目录下的这些隐藏文件,它们定义了你的 bash 环境,包括别名、路径、环境变量等。

4.常见的环境变量

环境变量    作用
PATH     命令搜索路径(最重要)
HOME     当前用户主目录
PWD     当前所在目录
LANG     系统语言 / 编码
SHELL     当前使用的 shell
LD_LIBRARY_PATH     动态库搜索路径(C/C++ 常用)

5.Linux 下查看 / 设置环境变量的命令

1.查看所有环境变量

env

2.查看某个环境变量

echo $PATH
echo $HOME
echo $LD_LIBRARY_PATH

echo $PATH  里的  $  是什么意思?

PATH  是一个环境变量的名字。

当你写  $PATH  时,Shell 会把它替换成这个变量里存的实际内容(也就是一长串目录路径)。

所以  echo $PATH  执行的效果,就是把  PATH  变量里存的所有路径打印出来。

如果不加 $,Shell会把PATH当成普通字符串打印

3.临时设置环境变量(关闭终端失效)

export MY_VAR="hello"
export PATH=$PATH:/new/path

4.

6.对环境变量的浅层了解

幕后的运行逻辑是这样的:


以前敲 text.exe 报错:
当你只输入 text.exe 时,系统不知道这个文件在哪。Linux 出于安全考虑,默认不会在当前目录下找程序。所以你必须加 ./(代表当前目录),告诉系统:“就在当前目录下运行这个程序!”


现在直接敲 text.exe 成功:
当你输入任何一个命令时,Linux 都会去一个叫 $PATH 的环境变量里记录的几个固定文件夹中,挨个去搜有没有同名的文件。

7.main函数的三个参数

// 标准三参数 main 函数
int main(int argc, char *argv[], char *envp[])

三个参数分别是:


argc:命令行参数的个数(整数)
argv:命令行参数的字符串数组
envp:系统环境变量字符串数组(第三个参数,最常用的扩展形式)

#include <stdio.h>

// 三参数 main
int main(int argc, char *argv[], char *envp[])
{
    // 1. 打印参数个数
    printf("参数个数 argc = %d\n", argc);

    // 2. 打印所有命令行参数
    printf("\n命令行参数 argv:\n");
    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }

    // 3. 打印前5个环境变量(envp 以 NULL 结尾)
    printf("\n环境变量 envp (前5个):\n");
    for (int i = 0; envp[i] && i < 5; i++) {
        printf("envp[%d] = %s\n", i, envp[i]);
    }

    return 0;
}

1.argc

2.argv[ ]

#include <stdio.h>
#include <string.h> // 必须引入这个头文件来使用 strcmp

int main(int argc, char *argv[], char *envp[])
{
    // 先确保用户至少输入了一个额外的命令行参数
    if (argc > 1) 
    {
        // argv[0] 是程序名自己("./text.exe")
        // argv[1] 才是你输入的第一个真正参数(比如 a 或 b)
        
        if (strcmp(argv[1], "a") == 0) // strcmp 返回 0 代表字符串完全相等
        {
            printf("a\n");
        }
        else if (strcmp(argv[1], "b") == 0)
        {
            printf("b\n");
        }
    }
    else 
    {
        printf("请在运行程序时传入参数 a 或 b!\n");
    }

    return 0;
}

2.argv[ ]

#include <stdio.h>

// 三参数 main,envp 指向环境变量数组
int main(int argc, char *argv[], char *envp[])
{
    printf("=== 前5个环境变量 ===\n");

    for (int i = 0; i < 5 && envp[i] != NULL; i++) {
        printf("envp[%d]: %s\n", i, envp[i]);
    }

    return 0;
}

8.程序中如何访问环境变量

1. 通过environ全局变量


environ是一个全局的字符指针数组,和argv类似,它指向所有环境变量字符串,最后以NULL结尾。

#include <stdio.h>

extern char **environ;

int main()
{
    for(int i = 0; environ[i] != NULL; i++)
    {
        printf("%s\n", environ[i]);
    }
    return 0;
}

2. 通过main函数的第三个参数

7.2演示过

3.通过getenv函数(推荐)

如果只想获取某个特定环境变量,用getenv最方便:

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

int main()
{
    printf("当前用户:%s\n", getenv("USER"));
    printf("家目录:%s\n", getenv("HOME"));
    printf("当前路径:%s\n", getenv("PWD"));
    return 0;
}

getenv("变量名")会返回该变量的值的字符串指针,如果变量不存在则返回NULL。

9.环境变量的特性与操作


1. 环境变量的作用域


环境变量有一个关键特性:被子进程继承,但不会反过来影响父进程


你在 bash 中设置的环境变量,会被 bash 启动的所有子进程(比如你的 C 程序)继承。


子进程修改环境变量,只会影响自己和它的子进程,父进程(bash)完全不受影响。


比如你在程序里修改了HOME,退出程序后,bash 里的$HOME还是原来的值。


2. 常用操作命令

echo $变量名:查看环境变量的值
export 变量名=值:设置环境变量,让它被子进程继承
unset 变量名:删除环境变量
env:查看所有环境变量

export MY_VAR="hello world"
echo $MY_VAR  # 输出 hello world
./your_program # 程序中用getenv("MY_VAR")就能获取到值
unset MY_VAR  # 删除变量
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

// main 三参数:argc, argv, envp(环境变量)
int main(int argc, char *argv[], char *envp[])
{
    pid_t pid = fork(); // 创建子进程

    if (pid == -1) {
        perror("fork失败");
        return 1;
    }

    if (pid == 0) {
        // ====================
        // 子进程
        // ====================
        printf("===== 子进程 PID = %d =====\n", getpid());
        for (int i = 0; i < 5 && envp[i] != NULL; i++) {
            printf("子进程 envp[%d] = %s\n", i, envp[i]);
        }
    } else {
        // ====================
        // 父进程
        // ====================
        printf("===== 父进程 PID = %d =====\n", getpid());
        for (int i = 0; i < 5 && envp[i] != NULL; i++) {
            printf("父进程 envp[%d] = %s\n", i, envp[i]);
        }

        // 等待子进程结束(避免输出混乱)
        wait(NULL);
    }

    return 0;
}

10.本地变量

1.特点:

只在当前函数里能用
存在栈(stack) 上
函数结束就销毁

2. fork () 之后,本地变量会发生什么?

父进程有的本地变量,子进程会全部复制一份
但:
✔ 复制完之后,父子的变量是完全独立的!
✔ 你改子进程的,父进程不受影响
✔ 你改父进程的,子进程也不受影响

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    // 父进程里的本地变量
    int a = 10;

    pid_t pid = fork(); // 创建子进程

    if (pid == 0) {
        // ==================
        // 子进程
        // ==================
        a = 20;  // 子进程改自己的 a
        printf("子进程:a = %d\n", a);
    }
    else {
        // ==================
        // 父进程
        // ==================
        wait(NULL); // 等子进程跑完
        printf("父进程:a = %d\n", a);
    }

    return 0;
}

终极结论


fork 之后:
子进程复制父进程的所有本地变量
但父子变量是独立的
一方修改,不会影响另一方


再用一句话总结
1.本地变量 = 各自的副本
2.fork = 完整复制一份
3.改自己的 = 不影响别人

11.常见误区与补充


修改环境变量的误区:

很多人以为在程序里修改环境变量会影响 bash,其实不会。因为 bash 启动程序时,会把环境变量复制一份给子进程,子进程的修改只在自己的地址空间里生效,不会写回父进程。


PATH的查找顺序:
bash 会按PATH里目录的顺序依次查找,先找到哪个目录下的可执行文件,就运行哪个。所以尽量不要把当前目录.加到PATH里,会有安全风险。


内建命令与外部命令:
有些命令(比如cd、export)是 bash 的内建命令,不是外部程序。它们直接在 bash 进程内部执行,所以可以修改 bash 的环境变量;而ls、cat这类外部命令,运行时是 bash 的子进程,无法修改 bash 的环境。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐