【Linux】命令行参数与环境变量

在 Linux 系统的学习与开发中,命令行参数环境变量是两个极为重要的概念,它们是程序与系统、程序与用户交互的关键桥梁。本文将从基本概念入手,逐步深入,结合代码示例详细解析这两个知识点。


一、环境变量:操作系统的"运行参数"

(一)基本概念

环境变量(environment variables)是操作系统中用来指定运行环境的参数集合。它有以下几个关键特性:

  • 特殊用途:比如在编译 C/C++ 程序时,链接器依靠环境变量查找动态 / 静态库,我们无需手动指定库路径也能完成链接。
  • 全局特性:在系统中具有全局性,影响范围广泛。

(二)常见环境变量

Linux 系统中有许多预定义的环境变量,以下是最常见的几个:

|----------|--------------------------------------|---------------------------------------------------|
| 环境变量 | 作用 | 示例 |
| PATH | 指定命令的搜索路径,系统会按该路径顺序查找可执行程序 | /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin |
| HOME | 指定用户的主工作目录,即用户登录系统后的默认目录 | 普通用户为/home/xxx,root 用户为/root |
| SHELL | 指定当前使用的 Shell,Linux 默认通常是/bin/bash | /bin/bash |

(三)查看环境变量的方法

最直接的方式是使用 echo命令,格式为echo $变量名。

  • 单变量查看:echo $变量名,如echo $PATH
  • 批量查看:env显示所有环境变量;set显示本地 Shell 变量和环境变量。

(四)举例

1、PATH环境变量测试

将以上简单的代码编译成可执行程序后,我们来对比两种执行方式:

  • 带路径执行:./hello
  • 直接执行:hello

问题来了,为什么系统命令(如lscd)可以直接执行,而我们的hello程序必须带路径?

那是因为系统命令的可执行文件位于PATH环境变量指定的路径中,系统会自动搜索;而hello所在路径不在PATH中,所以必须手动指定路径。

所以我们可以试试将程序路径加入PATH:

bash 复制代码
export PATH=$PATH:程序实际所在路径

直接执行hello,此时程序可以正常运行。

2、HOME环境变量测试

  • root 用户执行:echo $HOME,输出/root
  • 普通用户执行:echo $HOME,输出/home/xxx(xxx 为用户名)。

如下图,执行cd ~; pwd,输出的路径与$HOME的值一致,说明~是HOME的简写。

(五)与环境变量相关的命令

|--------|------------------------|
| 命令 | 功能 |
| echo | 显示单个环境变量的值 |
| export | 设置新的环境变量(或导出已有变量为环境变量) |
| env | 显示所有环境变量 |
| unset | 清除环境变量 |
| set | 显示本地定义的 Shell 变量和环境变量 |

(六)环境变量的组织方式

每个程序都会收到⼀张环境表,环境表是⼀个字符指针数组,每个指针指向⼀个以'\0'结尾的环境字符串。

(七)通过代码获取环境变量

在 C 语言中,有两种常见方式获取环境变量。

方式 1:通过main函数的第三个参数

main函数可以接收第三个参数char *env[],它指向环境变量表。

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;
}

运行的结果和使用env命令一样。

方式 2:通过全局变量environ

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;
}

libc中定义了全局变量environ,它指向环境变量表。由于environ未包含在头文件中,使用时需用extern声明。

(八)通过系统调用获取或设置环境变量

Linux 提供了系统调用函数来获取或设置环境变量,常用的有 getenv和 putenv(后面讲解)。

函数getenv:获取指定环境变量的值

函数原型:char *getenv(const char *name);

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>  // getenv需要的头文件
int main()
{
    printf("%s\n", getenv("PATH"));
    return 0;
}

(九)环境变量的全局属性

环境变量具有全局属性可以被子进程继承。我们通过一个例子来验证这一点。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
int main()
{
    char *env = getenv("MYENV");
    if(env){
        printf("%s\n", env);
    }
    return 0;
}

直接运行程序,无输出(因为MYENV环境变量不存在)。

此时我们导出环境变量MYENV:

bash 复制代码
export MYENV="hello world"

再次运行程序:

说明子进程(我们的程序)继承了父进程(Shell)的环境变量。

但当我们执行MYENV="hello world"(不export)时,这个变量仅属于当前 Shell 的本地变量 (Local Variable),其作用域仅限于当前 Shell 进程本身,不会被子进程(例如我们编译的 C 程序)继承。

原理分析:

  • Shell 进程在创建子进程时,会将自己的环境变量 (通过 export 声明的变量)复制一份传递给子进程,但本地变量不会被复制。
  • 因此,直接执行 MYENV="hello world" 后运行程序,子进程中无法获取 MYENV;而通过 export MYENV="hello world" 声明后,MYENV 成为环境变量,才能被子进程继承。

(十)环境变量的持久化设置

临时通过 export 设置的环境变量,会在当前 Shell 关闭后失效。若需环境变量永久生效(系统重启后仍可用),需修改系统配置文件,具体分为以下两种场景:

  • 对所有用户生效(全局设置)

修改 /etc/profile/etc/environment 文件(需 root 权限),所有用户登录后均可使用该环境变量。(以修改 /etc/profile 为例)

1、使用 root 权限打开文件

bash 复制代码
sudo vim /etc/profile

2、在文件末尾添加环境变量声明

bash 复制代码
export MYENV="hello world"  # 格式:export 变量名=值(值若含空格需用引号包裹)

3、无需重启,执行以下命令让系统重新加载配置文件:

bash 复制代码
source /etc/profile
  • 仅对当前用户生效(局部设置)

修改当前用户主目录下的 ~/.bashrc~/.bash_profile 文件(无需 root 权限),仅当前用户可使用该环境变量。(以修改 ~/.bashrc 为例)

1、打开文件

bash 复制代码
vim ~/.bashrc  # 或 gedit ~/.bashrc

2、在文件末尾添加环境变量声明

bash 复制代码
export MYENV="hello world"

3、使配置立即生效

bash 复制代码
source ~/.bashrc  # 重新加载当前用户的配置文件

注意事项:

  • 配置文件中的环境变量声明必须带 export,否则仅为文件所在 Shell 的本地变量,无法被子进程继承。
  • 若同时修改了全局和局部配置文件,局部配置会覆盖全局配置 (优先级:~/.bashrc > ~/.bash_profile > /etc/profile)。
  • 对于 ~/.bash_profile~/.bashrc 的区别:
    • ~/.bash_profile 仅在用户登录时加载 (如通过 ssh 登录或图形化界面登录);
    • ~/.bashrc每次打开新的终端窗口时加载,更适合日常临时添加的环境变量,推荐优先使用。

二、命令行参数

(一)基本概念

命令行参数是用户在启动程序时,通过命令行传递给程序的参数 ,用于控制程序的行为、指定输入输出等。例如ls -l /home中,-l/home都是ls命令的命令行参数。

(二)命令行参数在 C 程序中的表示

想必大家对这两个参数都很眼熟,但是我们并不知道是干什么用的。

C 程序通过main函数的前两个参数接收命令行参数

  • int argc参数个数(argument count),包含程序名本身。
  • char *argv[]参数数组 (argument vector),每个元素是一个参数字符串,argv[0]是程序名,argv[1]argv[argc-1]是用户传入的参数。

例如:

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

三、命令行参数与环境变量的区别与联系

|----------|----------------------------|---------------------------------|
| 对比项 | 命令行参数 | 环境变量 |
| 作用域 | 仅对当前启动的程序有效 | 具有全局属性,可被子进程继承 |
| 传递时机 | 程序启动时通过命令行传递 | 程序启动前已由系统或用户设置 |
| 修改方式 | 需重新启动程序并传入新参数 | 可通过export、修改配置文件等方式动态修改(或持久化) |
| 联系 | 两者都是程序与外部交互的方式,共同决定程序的运行行为 | |

相关推荐
Jack___Xue5 分钟前
LangChain实战快速入门笔记(五)--LangChain使用之Tools
笔记·microsoft·langchain
历程里程碑25 分钟前
C++ 10 模板进阶:参数特化与分离编译解析
c语言·开发语言·数据结构·c++·算法
老秦包你会32 分钟前
C++进阶------智能指针和特殊类设计方式
开发语言·c++
BullSmall35 分钟前
linux 根据端口查看进程和对应的应用
linux·运维
专注VB编程开发20年35 分钟前
C#内存加载dll和EXE是不是差不多,主要是EXE有入口点
数据库·windows·microsoft·c#
黑客思维者38 分钟前
为什么Linux常被提权操作?
linux·网络·安全
石像鬼₧魂石38 分钟前
Fail2Ban核心架构学习
linux·学习·ubuntu
阿阿越40 分钟前
Linux系统编程 -- 进程优先级、切换和调度
linux·运维·服务器
地狱为王41 分钟前
Windows下编译coturn
windows
Hey小孩43 分钟前
Linux审计组件:auditd
linux·运维