Linux操作系统从入门到实战(二十二)命令行参数与环境变量
前言
上一篇博客中,我们介绍了进程切换与进程调度;
- 这一篇,我们将讲解命令行参数与环境变量。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482
一、命令行参数
- 你有没有好奇过:
-
当我们在Linux终端输入
ls -l
或者cp file1.txt file2.txt
时,程序是怎么知道我们后面输入的-l
或file1.txt
是什么意思的?
这些跟在命令后面的“附加信息”,其实就是命令行参数。
1. 程序怎么知道我们在命令行输入了什么?
- 比如我们写了一个C语言程序,编译后生成可执行文件
test
,然后在终端运行./test hello 123
。 - 程序怎么知道我们输入了
hello
和123
呢?
这就要靠C语言中 main
函数的两个特殊参数:argc
和 argv
。
2. main 函数的参数 argc 和 argv 到底是什么?
我们平时写 main
函数可能会简化成 int main()
,但其实它的标准形式是这样的:
int main(int argc, char *argv[])
这两个参数专门用来接收命令行输入的信息:
argc
(Argument Count):简单说就是“参数的总个数”。
注意:它会把程序本身的路径也算作一个参数。
比如运行./test hello 123
时,输入的内容有3个部分:./test
(程序本身)、hello
、123
,所以argc = 3
。argv
(Argument Vector):可以理解为“参数的具体内容”,它是一个字符串数组(存放了所有参数的文本)。argv[0]
永远是程序自己的路径或名字(比如./test
);argv[1]
是第一个用户输入的参数(比如hello
);argv[2]
是第二个用户输入的参数(比如123
);- 以此类推,最后一个元素是
NULL
(表示数组结束)。
3. 用代码实际看看它们的值?
我们写一个简单的程序,把所有参数都打印出来:
#include <stdio.h>
int main(int argc, char *argv[]) {
// 打印参数的总个数
printf("总共有 %d 个参数\n", argc);
// 遍历所有参数并打印
for (int i = 0; i < argc; i++) {
printf("第 %d 个参数:%s\n", i, argv[i]);
}
return 0;
}
编译运行试试:
- 保存为
test.c
,用gcc test.c -o test
编译生成test
可执行文件; - 在终端输入
./test hello world 2025
,输出会是这样:
总共有 4 个参数
第 0 个参数:./test
第 1 个参数:hello
第 2 个参数:world
第 3 个参数:2025
4. 这些参数有什么实际用处?
我们平时用的Linux命令,本质上也是程序,它们就是通过 argc
和 argv
来处理我们输入的参数的。
比如:
- 输入
ls -l
时,ls
程序的argc=2
,argv[1] = "-l"
,程序看到-l
就知道要“以详细列表形式显示文件”; - 输入
cp a.txt b.txt
时,cp
程序的argc=3
,argv[1] = "a.txt"
,argv[2] = "b.txt"
,所以它知道要把a.txt
复制成b.txt
。
二、环境变量:
- 在终端输入
ls
就能执行列表命令,输入gcc
就能编译代码,为什么不用写全它们的安装路径(比如/bin/ls
)? - 还有,系统怎么知道你的用户名是什么、家目录在哪里?
- 这些背后其实都和“环境变量”有关。
1. 为什么输入 ls 就能运行,不用写全路径?
比如 ls
这个命令,实际安装在 /bin/ls
路径下,但我们不用输入 /bin/ls
,直接敲 ls
就行。这是谁在帮我们“找”到程序的位置?
- 答案就是 环境变量 里的
PATH
。 - 简单说,环境变量就像系统里的“全局记事本”,记录着各种程序运行时需要的配置信息,
PATH
就是其中之一——它里面存着一串目录(用冒号分隔), - 系统会在这些目录里自动搜索你输入的命令。
2. 什么是环境变量?为什么需要它?
环境变量 可以理解为“进程运行时的全局配置信息”,本质是一堆“键值对”(比如 KEY=VALUE
)。
它的核心作用有两个:
- 解耦路径依赖:就像
PATH
变量,不用硬记每个程序的安装路径,系统会自动按PATH
里的目录找程序(比如ls
、gcc
都在PATH
包含的目录里)。 - 传递全局状态:比如
USER
变量存着当前用户名(输入echo $USER
能看到),HOME
存着你的家目录(echo $HOME
显示/home/你的用户名
),LANG
控制语言编码……这些信息让不同程序能“统一认知”系统状态。
3. 在终端里怎么查看环境变量?
很简单,有两个常用命令:
看单个变量:
echo $变量名
。比如想知道PATH
里有哪些路径,输入echo $PATH
,会输出类似这样的内容(不同系统可能有差异):
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
看所有变量:输入
env
,会列出系统里所有环境变量(比如USER=xxx
、TERM=xterm
等)。
4. 如果我写C程序,想拿到环境变量的值,该怎么做?
- C语言提供了3种方式访问环境变量,重点记前两种就行:
方式1:用 getenv 精准查找(最常用)
getenv
就像“按名字查字典”,给它变量名,它返回对应的值。比如查 PATH
:
#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件
int main() {
// 查找名为"PATH"的环境变量
char *path_value = getenv("PATH");
if (path_value != NULL) {
printf("PATH里的路径:%s\n", path_value);
} else {
printf("没找到PATH变量!\n");
}
return 0;
}
编译运行后,就能看到和 echo $PATH
一样的内容。
方式2:用 environ 全局变量枚举所有变量
environ
是系统自带的全局变量,它指向一个数组,里面存着所有环境变量(格式都是 KEY=VALUE
)。比如遍历所有变量:
#include <stdio.h>
// 声明environ全局变量(系统已经定义好了)
extern char **environ;
int main() {
// 遍历数组,直到遇到NULL(数组结束标志)
for (int i = 0; environ[i] != NULL; i++) {
printf("环境变量[%d]:%s\n", i, environ[i]);
}
return 0;
}
运行后会打印出所有环境变量,和 env
命令的输出类似。
方式3:修改环境变量
如果想在程序里改环境变量,可以用 setenv
(设置或覆盖)和 unsetenv
(删除):
#include <stdlib.h>
#include <stdio.h>
int main() {
// 设置一个新变量MY_NAME,值为"xiaobai",第三个参数1表示覆盖已存在的
setenv("MY_NAME", "xiaobai", 1);
printf("MY_NAME: %s\n", getenv("MY_NAME")); // 会输出xiaobai
// 删除MY_NAME变量
unsetenv("MY_NAME");
printf("删除后MY_NAME: %s\n", getenv("MY_NAME")); // 会输出(null)
return 0;
}
5. 环境变量和“本地变量”有啥区别?
在终端里,我们可能会用 A=123
或 export B=456
设变量,但这两种不一样:
对比项 | 环境变量(用 export 声明) |
本地变量(直接 KEY=VALUE ) |
---|---|---|
作用范围 | 当前进程和它启动的子进程都能读到 | 只有当前终端会话能读到,子进程读不到 |
举个例子 | 终端输入 export B=456 ,再运行C程序,用 getenv("B") 能拿到456 |
终端输入 A=123 ,运行C程序,getenv("A") 拿不到值 |
6. 环境变量的“全局”和“会话”是什么意思?
环境变量不是一成不变的,按影响范围分两种:
全局环境变量:影响所有用户和所有进程,比如
/etc/environment
文件里的配置。系统级的程序(比如ls
、cp
)的路径通常在这里配置,确保所有用户都能正常使用。会话环境变量:只影响当前用户的终端会话,比如
~/.bashrc
(bash终端的配置文件)里的内容。你可以在这里设自己的别名(比如alias ll='ls -l'
)、自定义路径,只对自己生效。
注意:修改配置文件后,需要用 source ~/.bashrc
(比如修改了 .bashrc
)让配置立即生效,否则要重启终端才会生效。
7. 环境变量是怎么在进程之间“传递”的?
环境变量的传递规则很简单:
- 每个进程启动时,会复制一份父进程的环境变量(相当于“继承”)。比如终端(父进程)里的环境变量,你运行的C程序(子进程)能读到。
- 子进程修改自己的环境变量,不会影响父进程(副本之间互不干扰)。
8. 环境变量和之前说的“命令行参数”有啥不一样?
简单说,命令行参数是“临时指令”,环境变量是“全局配置”,对比表如下:
特性 | 命令行参数(比如 ls -l 中的 -l ) |
环境变量(比如 PATH ) |
---|---|---|
作用 | 给程序传递一次性的输入(本次运行有效) | 给程序传递全局配置(进程及子进程有效) |
用法 | 运行程序时显式写在后面(./程序 参数 ) |
预先通过 export 或配置文件设置 |
举个例子:curl -X GET $API_URL
中,-X GET
是命令行参数(告诉curl用GET方法),$API_URL
是环境变量(全局配置接口地址,换个程序也能复用)。
三、命令行参数 vs 环境变量
特性 | 命令行参数 | 环境变量 |
---|---|---|
传递目的 | 临时控制程序单次运行的行为(如参数、选项) | 提供进程运行时的全局配置信息(如路径、密钥) |
作用范围 | 仅对当前执行的程序实例有效,执行结束后失效 | 对当前进程及所有子进程有效(通过继承机制传递) |
使用方式 | 运行程序时显式附加在命令后(如 python script.py --debug ) |
预先通过 export 命令设置或写入配置文件(如 .bashrc ) |
可见性 | 在进程列表(如 ps 命令)中可见,易被查看 |
需通过 printenv 或进程环境文件查看,相对隐蔽 |
持久性 | 完全临时,仅随当前命令执行存在 | 可临时(当前shell会话)或持久(配置文件)存在 |
继承性 | 不会被子进程自动继承,需显式传递给子进程 | 子进程会自动继承父进程的环境变量 |
数据量适配 | 适合传递少量、简单的参数(如选项、路径) | 适合传递多组配置信息(如多个服务地址、密钥) |
典型示例 | ls -l 中的 -l 、curl -o file.txt 中的 -o file.txt |
PATH (可执行文件路径)、HOME (用户主目录)、API_KEY (接口密钥) |
以上就是这篇博客的全部内容,下一篇我们将继续探索Linux的更多精彩内容。
我的个人主页
欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482
非常感谢您的阅读,喜欢的话记得三连哦 |