C/C++命令行参数和环境变量的使用

📅 2026/7/9 12:08:19 👁️ 阅读次数 📝 编程学习
C/C++命令行参数和环境变量的使用

C/C++命令行参数和环境变量的使用

  • 命令行参数
  • Linux环境变量
    • 环境变量的存在
    • 进程获取环境变量的方法
    • 用户程序像指令一样使用
    • 程序安装永久添加指令
    • 环境变量有关配置文件
    • 本地变量
      • export指令和本地变量
      • 修改配置文件添加环境变量
    • 和环境变量相关的命令
      • 环境变量有关的常用命令
      • 内建命令

命令行参数

命令行参数(也称为位置参数)是用户在启动一个可执行程序、命令或脚本时,在命令名后面输入的额外信息。这些信息用于向程序传递数据、选项或配置,从而改变程序的行为。

在 C 语言中,main函数是进程的入口,进程启动时,命令行参数的各个信息都保存在main函数的前 2 个形参中。

同样取决于编译器,至少 gcc 系列和 MSVC(Visual Studio 2019) 是如此,其他的编译器不保证。

其他的例如嵌入式环境(直白点说就是 “裸机” 环境),程序入口点可能不是main函数,形参处同样为void表示空。

还有远古时代的编译器以void main(){}作为程序入口,这种也没有存储命令行参数的形参。

例如这里,打印命令行参数的信息。

#include<stdio.h>intmain(intargc,char*argv[]){for(inti=0;i<argc;i++)printf("%d %s\n",i,argv[i]);printf("\nhello, world.\n");return0;}

交互界面:

[Bjarne@VM-8-8-centos cTest]$ ./a.exe0./a.exe hello, world.[Bjarne@VM-8-8-centos cTest]$

会发现在 Shell 的命令以字符串的形式,可通过argv数组获取。

指令都可以通过新增选项来实现不同的功能,都是在启动进程时进行添加。所以在启动进程时,添加的选项,只要用空格作为分隔符进行隔开,都可以被进程识别。

[Bjarne@VM-8-8-centos cTest]$ ./a.exe-a--b---c--version# 至少1个空格0./a.exe1-a2--b3---c4--versionhello, world.[Bjarne@VM-8-8-centos cTest]$

这个识别工作是由 Shell 去完成。即通过命令行启动的子进程的命令行参数和后文要提到的环境变量,都是父进程bash传递给子进程的。简单理解就是 bash 会传递给main函数两张表,一张命令行参数表(argv),另一张是环境变量表(env)。

在命令中执行指令时,至少要传一个程序名称,所以argv[0]就是指定的程序名称。

通过这个特性,可以实现一个程序,通过不同的选项来指定这个程序要完成的功能。

例如简易的mytouch命令:

#include<stdio.h>intmain(intargc,char*argv[]){if(argc<2)//命令行参数至少有2个return-1;fclose(fopen(argv[1],"w"));return0;}

交互界面:

[Bjarne@VM-8-8-centos cTest]$ gcc a.c-omytouch[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake[Bjarne@VM-8-8-centos cTest]$ ./mytouch t.txt[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake t.txt[Bjarne@VM-8-8-centos cTest]$

Linux环境变量

用户自己写的程序,需要指定可执行程序的相对路径才能运行(例如,带个./指定可执行程序在当前位置)。而指令只需要输入指令名就可以运行。

所以执行一个程序需要指定程序的位置。而lspwd这些指令存在一个默认搜索路径,Shell 通过这个搜索路径就能找到指令所在。所以环境变量就是用来记录这个搜索路径的工具。

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。

例如,程序员编写 C/C++ 代码的时候,在链接的时候,从来不知道所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性,即在操作系统的命令行交互界面可查看,以及能被所有进程识别

环境变量的存在

指令echo $NAME:查看环境变量 ”NAME“ 的内容。若不加$echo会将后文识别为普通字符串。

常见的环境变量:

  • PATH:指定命令的搜索路径
  • HOME:指定用户的主工作目录(即用户登陆到 Linux 系统中时,默认的目录)。普通用户则是/home/username,root 用户则是/root
  • SHELL:当前 Shell,它的值通常是/bin/bash
  • PWD:存储着当前工作目录的绝对路径。指令pwd会从 PWD 中读取工作目录。

可以看到,一般环境变量都是大写的。在用户登录时会从配置文件读取环境变量并生成。登录的大致流程如下:

  1. 输入用户名和密码。
  2. 认证。
  3. 认证成功,创建会话,读取配置文件形成环境变量(不止一个,PATH,PWD,HOME等)。例如根据用户名初始化 HOME , root 账户HOME=/root,普通账户HOME/XXX
  4. cd $HOME

交互界面查看环境变量:

[Bjarne@VM-8-8-centos cTest]$echo$PATH/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin[Bjarne@VM-8-8-centos cTest]$echo$PWD/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$cd$HOME[Bjarne@VM-8-8-centos ~]$echo$PWD/home/Bjarne[Bjarne@VM-8-8-centos ~]$

通过echo指令看到的环境变量的内容是由:作为分隔符的路径字符串组成的字符串。

当用户输入指令时,例如ls,则 Shell 或操作系统会按环境变量的顺序在路径下搜索ls指令,找不到则继续搜索下一个路径。

还可以通过指令env查看所有环境变量。

[Bjarne@VM-8-8-centos ~]$envXDG_SESSION_ID=133145# 省略USER=Bjarne# 省略PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/binPWD=/home/Bjarne# 当前工作目录LANG=en_US.utf8# 语言SHLVL=1HOME=/home/Bjarne# 当前用户的家目录# 省略[Bjarne@VM-8-8-centos ~]$

进程获取环境变量的方法

这里主要记录 3 个常用的方法:

  1. C 语言的库函数getenv
  2. main函数的第 3 个参数char* env[]
  3. 外部全局变量char** environ

程序可以通过库函数getenv获取环境变量的值。

main函数也支持第 3 个形参用于存储环境变量,但不是所有的编译器都支持第 3 个形参,也不是因此进程获取环境变量最合适的方法还是getenv,这个库函数被写进 C语言的标准中,流通的编译器都会遵循 C 语言的标准。

#include<stdio.h>#include<stdlib.h>// 使用库函数getenv需要展开头文件stdlib.h// Windows下的MSVC也可用intmain(intargc,char*argv[],char*env[]){for(inti=0;env[i];i++)printf("%s\n",env[i]);printf("\n");printf("%s\n",getenv("PATH"));return0;}

交互界面:

[Bjarne@VM-8-8-centos cTest]$ ./a.exeXDG_SESSION_ID=133145HOSTNAME=VM-8-8-centos# 省略大量信息OLDPWD=/home/Bjarne# 单独输出PATH的值/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin[Bjarne@VM-8-8-centos cTest]$

还可以通过extern关键字导入外部变量environ获取环境变量。libc中(Linux自带的标准库)定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。注意 Windows 的环境变量指针是_environ,所以environ可以在 Linux 使用,但在 Windows 需要使用_environ

#include<stdio.h>#include<stdlib.h>intmain(intargc,char*argv[]){externchar**environ;// Windows换成_environinti=0;for(;environ[i];i++){printf("%s\n",environ[i]);}return0;}

程序运行效果和命令行参数env没有区别,这里就不演示交互界面了。

environ变量的环境变量的组织方式:

每个程序都会收到一张环境变量表命令行参数表。环境表和命令行参数表都是字符指针数组,每个指针元素指向一个以\0结尾的环境字符串,最后一个指针元素指向 NULL。

至于argv最后一个元素指向 NULL,依旧有argc这个冗余的形参存在,是因为历史的原因。

换个角度想,argc可以表示命令行参数的数量,减少了直接遍历argv统计数量的情况,使得程序可以完成更多功能。

知道了如何获取环境变量,程序也可以变得更灵活。例如某个程序只允许 root 账户登录,则设置条件判断。这也是很多指令为什么会设置选项的原因。

intmain(){char*who=getenv("USER");if(strcmp(who,"root")!=0){//用户非root不允许执行printf("非法用户\n");return0;}//...return0;}

用户程序像指令一样使用

一般情况下,路径并没有将用户的程序添加到环境变量中,所以需要指定相对路径来让操作系统找到程序的位置。

让用户程序像指令一样使用,可以通过赋值的方式修改环境变量的值。赋值方式:

PATH=/home/Bjarne/work/cTest

例如原来的 mytouch 可执行程序:

[Bjarne@VM-8-8-centos cTest]$echo$PATH# 查看环境变量的值/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin[Bjarne@VM-8-8-centos cTest]$pwd/home/Bjarne/work/cTest# 查看当前路径,并覆盖环境变量[Bjarne@VM-8-8-centos cTest]$PATH=/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$echo$PATH/home/Bjarne/work/cTest# 用户的程序不需要指定相对路径也能使用[Bjarne@VM-8-8-centos cTest]$ mytouch t.txt[Bjarne@VM-8-8-centos cTest]$ls# 覆盖PATH的代价是找不到原来的部分指令-bash: ls:commandnot found[Bjarne@VM-8-8-centos cTest]$\ls -bash: ls:commandnot found# 只能通过相对或绝对路径来找[Bjarne@VM-8-8-centos cTest]$ /usr/bin/ls# 这里不会显示文件高亮区分文件a.c a.exe makefile mytouch oldMake t.txt# 文件t.txt创建成功[Bjarne@VM-8-8-centos cTest]$ ./../../../../usr/bin/ls# 相对路径a.c a.exe makefile mytouch oldMake t.txt[Bjarne@VM-8-8-centos cTest]$PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin# 还原环境变量的值[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake t.txt[Bjarne@VM-8-8-centos cTest]$

很多时候我们作为用户,并不是想要覆盖 PATH 的值,而是将我们的程序也加到环境变量中,所以需要使用变量让 Shell 识别原来的值,即赋值时用$PATH表示 PATH 原来的值。

例如,添加新路径到环境变量

PATH=$PATH:/home/Bjarne/work/cTest

交互界面:

[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake[Bjarne@VM-8-8-centos cTest]$echo$PATH/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin[Bjarne@VM-8-8-centos cTest]$pwd/home/Bjarne/work/cTest# 将新地址添加到环境变量中[Bjarne@VM-8-8-centos cTest]$PATH=$PATH:/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$echo$PATH/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin:/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$ mytouch t.txt[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake t.txt[Bjarne@VM-8-8-centos cTest]$

删除环境变量同理,可以通过覆盖环境变量的值来实现。

程序安装永久添加指令

之前的修改环境变量的方法都是修改内存中的环境变量的信息,或者说改的是 bash 进程内部的环境变量信息。

所以之前的修改环境变量都是暂时的,关闭终端后内存数据被清理,再启动时会恢复原来的 PATH。因为操作系统每次启动都会从自带的脚本配置文件读取预设的数据生成环境变量

[Bjarne@VM-8-8-centos cTest]$pwd/home/Bjarne/work/cTest# 修改环境变量[Bjarne@VM-8-8-centos cTest]$PATH=$PATH:/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$echo$PATH/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin:/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$# 这里关闭xshell[Bjarne@VM-8-8-centos cTest]$echo$PATH# 再启动时环境变量恢复原样/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin[Bjarne@VM-8-8-centos cTest]$

父进程(例如 bash)把命令行参数和环境变量要传递给子进程,前提条件是这个父进程本身有数据。而父进程的数据来源是磁盘上的配置文件。

所以更改环境变量仅限于本次登录,关闭 Shell 将会重启所有环境变量。想让用户自身的进程和指令一样生效,可以将可执行程序移动到环境变量指定的目录下

例如,将之前的mytouch移动到/usr/bin目录下。

[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake[Bjarne@VM-8-8-centos cTest]$sudomvmytouch /usr/bin/mytouch# 移动可执行文件mytouch需要root权限[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile oldMake[Bjarne@VM-8-8-centos cTest]$ mytouch t.txt[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile oldMake t.txt[Bjarne@VM-8-8-centos cTest]$

这种将可执行程序移植到PATH或其他环境变量包含的目录下的行为,可以理解为最简易的程序安装,从 PATH 包含的目录下移出可执行程序同样可以理解为最简易的程序卸载

环境变量有关配置文件

但程序安装用的还是原来的环境变量。想要添加环境变量,还是需要修改配置文件。这个配置文件可以在家目录中找到。

[Bjarne@VM-8-8-centos cTest]$cd$HOME[Bjarne@VM-8-8-centos ~]$ls-al|grepbash_profile -rw-r--r--1Bjarne Bjarne193Apr12020.bash_profile[Bjarne@VM-8-8-centos ~]$

.bash_profile内部是脚本语言。脚本语言也是一门计算机语言,有自己的判断、循环和函数。用户把要执行的若干条命令写在一个配置文件里,然后交给Shell,让 Shell 把写好的命令从上往下按顺序执行,这个配置文件就叫做Shell脚本

在 Shell 启动,登录账号时,会自动执行这个文件里的内容,从而形成环境变量信息。

[Bjarne@VM-8-8-centos ~]$cat.bash_profile# .bash_profile# Get the aliases and functionsif[-f~/.bashrc];then.~/.bashrcfi# User specific environment and startup programsPATH=$PATH:$HOME/.local/bin:$HOME/binexportPATH[Bjarne@VM-8-8-centos ~]$

从这里就可以看到 PATH 环境变量的初始配置了。从.bash_profile文件中可以找到,这个配置文件还会先执行另一个.bashrc配置文件。

和环境变量有关的文件还有一个.bashrc

[Bjarne@VM-8-8-centos ~]$ls-al|grepbashrc -rw-r--r--1Bjarne Bjarne354Sep620:53 .bashrc[Bjarne@VM-8-8-centos ~]$

有了配置文件.bashrc.bash_profile后,便可开始添加永久的环境变量了。

本地变量

export指令和本地变量

bash提供的交互界面中,允许用户设置本地变量

[Bjarne@VM-8-8-centos cTest]$MYENV=helloworld[Bjarne@VM-8-8-centos cTest]$echo$MYENVhelloworld[Bjarne@VM-8-8-centos cTest]$a=3[Bjarne@VM-8-8-centos cTest]$b=5[Bjarne@VM-8-8-centos cTest]$c=53[Bjarne@VM-8-8-centos cTest]$echo$a3[Bjarne@VM-8-8-centos cTest]$echo$c53[Bjarne@VM-8-8-centos cTest]$

这些本地变量同样是在内存中申请的,并没有保存到磁盘,下次重启Shell时还是会消失,查询环境变量也查不到这些本地变量。

[Bjarne@VM-8-8-centos cTest]$MYENV=helloworld[Bjarne@VM-8-8-centos cTest]$echo$MYENVhelloworld[Bjarne@VM-8-8-centos cTest]$env|grepMYENV[Bjarne@VM-8-8-centos cTest]$

想让它变成环境变量,还需要使用export命令。

[Bjarne@VM-8-8-centos cTest]$MYENV=helloworld[Bjarne@VM-8-8-centos cTest]$echo$MYENVhelloworld[Bjarne@VM-8-8-centos cTest]$exportMYENV[Bjarne@VM-8-8-centos cTest]$env|grepMYENVMYENV=helloworld# 可以在环境变量表中找到自定义的环境变量了[Bjarne@VM-8-8-centos cTest]$

使用export命令添加环境变量后也能被后续的子进程给识别。例如这个代码输出环境变量:

#include<stdio.h>intmain(intargc,char*argv[]){externchar**environ;inti=0;for(;environ[i];i++){printf("%s\n",environ[i]);}return0;}

交互界面:

[Bjarne@VM-8-8-centos cTest]$MYENV=$PWD[Bjarne@VM-8-8-centos cTest]$echo$MYENV/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$env|grepMYENVMYENV=/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake[Bjarne@VM-8-8-centos cTest]$ gcc a.c-oa.exe-std=c99[Bjarne@VM-8-8-centos cTest]$ ./a.exe# 省略PWD=/home/Bjarne/work/cTestMYENV=/home/Bjarne/work/cTest# 找到了自己定义的环境变量HISTTIMEFORMAT=%F %T# 省略[Bjarne@VM-8-8-centos cTest]$

修改配置文件添加环境变量

但即使是export,也只是将本地变量导入bash进程上下文中重启后导入的本地变量还是会消失

所以这里修改配置文件.bash_profile

[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake[Bjarne@VM-8-8-centos cTest]$vim~/.bash_profile[Bjarne@VM-8-8-centos cTest]$cat~/.bash_profile# 省略...exportMYENV=/home/Bjarne/work/cTest# 这个就是用户添加的环境变量PATH=$PATH:$HOME/.local/bin:$HOME/bin:$MYENV# 将新增的MYENV添加到PATH中exportPATH[Bjarne@VM-8-8-centos cTest]$

此时再将 Shell 重启,Shell 会按照修改过的配置文件读取环境变量信息并导入内存。之后在命令行启动的子进程,以及子进程再创建的子进程,都会继承这个环境变量。

例如这里将 cTest 目录添加到 PATH 中,再重启后发现自己创造的可执行程序也能像正常指令一样使用。

[Bjarne@VM-8-8-centos cTest]$ls# 这里已经重启过a.c a.exe makefile mytouch oldMake[Bjarne@VM-8-8-centos cTest]$echo$PATH# 再看PATH环境变量/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/bin:/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$pwd/home/Bjarne/work/cTest[Bjarne@VM-8-8-centos cTest]$ mytouch t.txt[Bjarne@VM-8-8-centos cTest]$lsa.c a.exe makefile mytouch oldMake t.txt

所以说这些被继承的环境变量叫做系统环境变量,这些系统环境变量通常具有全局属性,即可被所有进程识别。

本地变量和环境变量的区别:

  • 本地变量在内存中生成,只在 bash 进程内部有效,不会被子进程继承下去。
  • 环境变量通过让所有子进程继承的方式,实现自身的全局属性。

和环境变量相关的命令

环境变量有关的常用命令

相关命令有很多,这里介绍5个常用的。后续有机会再补充。

  1. echo $ENV_NAME:显示某个环境变量ENV_NAME的值。

  2. export ENV_NAMEexport ENV_NAME=The var's value.:设置一个新的环境变量。允许先赋值,再设置。

  3. env:显示所有环境变量。

这 3 个在上文已经介绍过。

  1. unset:清除环境变量。$ unset VAR

  2. set:显示本地定义的shell变量和环境变量。

交互界面:

[Bjarne@VM-8-8-centos cTest]$exportVAR=114514[Bjarne@VM-8-8-centos cTest]$env|grepVARVAR=114514[Bjarne@VM-8-8-centos cTest]$unsetVAR# 解除环境变量[Bjarne@VM-8-8-centos cTest]$env|grepVAR# 可以看到,无法查到[Bjarne@VM-8-8-centos cTest]$

单独输入set同样可以查询当前 shell 的所有本地变量和环境变量。

[Bjarne@VM-8-8-centos cTest]$alphago=mts[Bjarne@VM-8-8-centos cTest]$exportVAR=114514[Bjarne@VM-8-8-centos cTest]$set|grepalphagoalphago=mts# set 输出内容太多,用grep筛选[Bjarne@VM-8-8-centos cTest]$set|grepVARVAR=114514[Bjarne@VM-8-8-centos cTest]$set|grepPATH=/PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/Bjarne/.local/bin:/home/Bjarne/binTMPPATH=/home/Bjarne/.cache/abrt/lastnotification.4ecREFqF[Bjarne@VM-8-8-centos cTest]$

这些指令还有更深层次的用途,但很多时候不一定用得上,后期有机会单独出一篇。

内建命令

部分指令例如echo,它并不像grep那样,在启动指令时会创建进程,因此即使将 PATH 环境变清空后,echo也能使用。

[Bjarne@VM-8-8-centos cTest]$PATH=''[Bjarne@VM-8-8-centos cTest]$echo$PATH[Bjarne@VM-8-8-centos cTest]$ls-bash: ls: No suchfileor directory[Bjarne@VM-8-8-centos cTest]$

执行echo就相当于执行bash进程内部的一个函数(或 shell 自身的一部分,bash 在变成进程前也是 C 语言代码)。这样的命令被称为内建命令

其他的通过fork等系统调用生成子进程才能生效的命令,叫做外部命令