欢迎来到我的频道 【点击跳转专栏】
码云链接 【点此转跳】
文章目录
- [1. 命令行参数](#1. 命令行参数)
- [1.1 是什么](#1.1 是什么)
- [1.2 为什么要有](#1.2 为什么要有)
- [1.3 有关命令行参数的3个细节](#1.3 有关命令行参数的3个细节)
- 2.环境变量
- [2.1 环境变量的基本概念](#2.1 环境变量的基本概念)
- [2.2 常⻅环境变量](#2.2 常⻅环境变量)
- [2.2.1 PATH&&查看和修改环境变量⽅法](#2.2.1 PATH&&查看和修改环境变量⽅法)
- [2.2.2 env(查看所有环境变量)](#2.2.2 env(查看所有环境变量))
- [2.2.3 浅谈上述打印的环境变量](#2.2.3 浅谈上述打印的环境变量)
- [2.3 通过代码如何获取环境变量](#2.3 通过代码如何获取环境变量)
- [2.3.1 命令⾏第三个参数](#2.3.1 命令⾏第三个参数)
- [2.3.2 通过第三⽅变量environ获取](#2.3.2 通过第三⽅变量environ获取)
- [2.3.3 getenv(最常用的方法)](#2.3.3 getenv(最常用的方法))
- [2.4 环境变量的全局属性](#2.4 环境变量的全局属性)
- [2.4.1 bash进程的环境变量怎么来的](#2.4.1 bash进程的环境变量怎么来的)
- [2.4.2 export(创建环境变量)](#2.4.2 export(创建环境变量))
- [2.4.3 unset(删除环境变量)](#2.4.3 unset(删除环境变量))
- [2.4.4 set(查看环境和局部变量)](#2.4.4 set(查看环境和局部变量))
- [2.4.5 echo怎么打印本地变量的?](#2.4.5 echo怎么打印本地变量的?)
1. 命令行参数
1.1 是什么
main函数能不能有参数?
答案是有的。
cpp
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
printf("argc: %d\n", argc);
int i = 0;
for(; i< argc ; i++)
{
printf("argv[%d]->%s\n", i, argv[i]);
}
return 0;
}
| 部分 | 说明 |
|---|---|
argc |
Argument Count:命令行参数的个数(包括程序名本身) |
argv |
Argument Vector:一个字符串数组,每个元素是一个命令行参数 |
运行结果:
它的本质,就是提取你输入的字符串(数字会自动转换成
char))然后根据空格断开 自动提取对应的内容 存入到char* argv[]数组中:
⚠️:不过需要注意的是,我们是看不到这个过程的,这个是通过bash和系统自动完成的!
1.2 为什么要有
看个例子就明白了:
cpp
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("该命令使用错误! 你应该这样用: %s -a|-b|-c|-d\n", argv[0]);
return 1;
}
if(strcmp(argv[1], "-a") == 0)
{
printf("我现在执行的是该命令的第一种功能\n");
}
else if(strcmp(argv[1], "-b") == 0)
{
printf("我现在执行的是该命令的第二种功能\n");
}
else if(strcmp(argv[1], "-c") == 0)
{
printf("我现在执行的是该命令的第3种功能\n");
}
else
{
printf("我现在执行的是该命令的默认功能\n");
}
return 0:
}
运行结果:
是不是跟
ls -a、ls -l和ls -n等等特别像!
💡:命令行参数 的本质应用,是为了实现一个命令,可以根据不同的选项,实现不同的子功能!也是Linux中所有命令选项功能的实现方式!!!
1.3 有关命令行参数的3个细节
- 细节1:命令行参数,至少是
1,argc >=1,argv[0]一定会有元素,指向的就是程序名! - 细节2:选项,是以空格 分隔的字符串,一个字符,也是字符串!
- 细节3:参数有
argc个,argv[argc-1]是最后一个字符串 ,而argv[argc] == NULL
2.环境变量
2.1 环境变量的基本概念
环境变量 (environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,属于系统级别的变量 ,变量名和变量内容,往往具有全局性!
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
2.2 常⻅环境变量
2.2.1 PATH&&查看和修改环境变量⽅法
在我们日常输入命令的时候,只需要像ls一样直接输入命令就行了,而我们自己写的程序却必须要加上./这是为什么呢?
系统的命令大多都是C语言写的可执行文件,并且放在了
usr/bin路径下,不要带./即可跑,这是因为它在usr/bin路径,是系统默认会自动匹配寻找的路径;如果我们把我们自己写的code文件,拷贝到usr/bin路径下,我们发现我们也能直接不带./就能运行!
这是因为我们系统执行程序前,需要将程序加载到内存中,在加载前,系统得找到程序在哪里,不过系统不会在当前目录下找,而是在特定路径 下找!
./本质是告诉系统你的程序在哪里!`
Linux系统怎么知道在哪个路径寻找?谁告诉你的?还会不会去别的路径找呢?
Linux存在一个叫环境变量的东西 ,是一个在全系统中全局有效的变量,属于shell语法自己定义的变量!而这里起作用的变量就叫:PATH!
PATH: 指定命令的搜索路径,就是告诉吸引,如果用户执行一个可执行文件,没有指明路径,就会去PATH所指向的路径一个个找!
PATH是其的变量名,我们可以用echo $环境变量名称查看内容(以:作为分隔符):
我们可以用
环境变量名=XXX的方式直接覆盖环境变量就行了我们发现
code可以直接运行,而ls无法运行了 这里直接把终端重启就恢复了
我么可以用
环境变量名=$环境变量名:XXX的方式增加新的内容,这样原本的命令(ls等)也不会受到影响了
2.2.2 env(查看所有环境变量)
shell
env : 显⽰所有环境变量
下面会展示显示内容并对变量添加介绍注释!
shell
fcy@VM-16-15-ubuntu:~$ env
# 当前用户使用的 shell 程序
SHELL=/bin/bash
# 历史命令保存的最大条数(此处为 1000 条)
HISTSIZE=1000
# 历史命令时间戳格式:%F 表示 YYYY-MM-DD,%T 表示 HH:MM:SS
HISTTIMEFORMAT=%F %T
# 当前工作目录(即用户执行 env 命令时所在的路径)
PWD=/home/fcy
# 当前登录用户名(等同于 whoami 的输出)
LOGNAME=fcy
# 当前会话类型(tty 表示通过终端/SSH 登录,而非图形界面)
XDG_SESSION_TYPE=tty
# 表示系统在登录时已显示"每日提示"(Message of the Day, MOTD)
MOTD_SHOWN=pam
# 用户主目录路径
HOME=/home/fcy
# 系统语言和字符编码(美式英语 + UTF-8)
LANG=en_US.UTF-8
# ls 命令中不同文件类型的显示颜色配置(例如目录蓝色、可执行文件绿色等)
LS_COLORS=rs=0:di=01;34:ln=01;36:...(省略部分为各类文件扩展名的颜色规则)
# 每次命令执行后自动追加到历史文件(确保多终端间历史同步)
PROMPT_COMMAND=history -a; history -a;
# SSH 连接信息:客户端IP 客户端端口 服务器IP 服务器端口
SSH_CONNECTION=49.80.114.131 29107 10.0.16.15 22
# less 命令关闭时调用的脚本(用于清理临时文件等)
LESSCLOSE=/usr/bin/lesspipe %s %s
# 会话类别(普通用户会话)
XDG_SESSION_CLASS=user
# 终端类型(支持 256 色的 xterm 兼容终端)
TERM=xterm-256color
# less 命令打开文件前调用的预处理器(如解压 .gz 文件再查看)
LESSOPEN=| /usr/bin/lesspipe %s
# 当前用户名(与 LOGNAME 相同)
USER=fcy
# Shell 嵌套层级(1 表示这是最外层 shell)
SHLVL=1
# 当前会话的唯一 ID(由 systemd-logind 分配)
XDG_SESSION_ID=301948
# 用户运行时目录(存放 socket、pid 等临时运行文件)
XDG_RUNTIME_DIR=/run/user/1006
# SSH 客户端连接信息:IP 地址、源端口、目标端口
SSH_CLIENT=49.80.114.131 29107 22
# 应用程序数据目录搜索路径(用于查找图标、桌面文件等)
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
# 系统可执行程序的搜索路径(最后多了自定义路径)
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/fcy/linux_code/lesson18
# D-Bus 会话总线地址(用于进程间通信)
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1006/bus
# 当前 SSH 会话绑定的伪终端设备
SSH_TTY=/dev/pts/0
# 最后执行的命令路径(这里显示的是 /usr/bin/env 本身)
_=/usr/bin/env
2.2.3 浅谈上述打印的环境变量
我们可以用history查看历史命令 而其记录的上线就是通过HISTSIZE这个环境变量控制的!
HISTSIZE:历史命令保存的最大条数。
而当我们whoami和pwd的时候 本质也是打印对应的环境变量 USER和PWD
USER:当前用户名PWD:当前工作目录
其中SHELL=/bin/bash,我们cd ~为什么能回到家目录 也是因为HOME 记录了我么的家目录位置。
SHELL: 当前Shell,它的值通常是/bin/bash(也就是具体的shell是什么)HOME: 指定⽤⼾的主⼯作⽬录
⚠️:其实还有很多比如
cd -等等 但原理大差不差就不写了!
2.3 通过代码如何获取环境变量
| 方法 | 可移植性 | 是否需要 main 参数 |
说明 |
|---|---|---|---|
char *env[] in main |
❌(仅 POSIX/GCC) | ✅ | 直接遍历所有环境变量 |
extern char environ |
❌(POSIX) | ❌ | 全局变量,可在任意函数使用 |
getenv("KEY") |
✅(C 标准) | ❌ | 只能查单个变量,最安全 |
2.3.1 命令⾏第三个参数
其实在
main中还存在第三个命令行参数char* env[]
| 参数 | 含义 |
|---|---|
argc |
命令行参数个数(argument count) |
argv |
命令行参数数组(argument vector),argv[0] 是程序名 |
env |
环境变量表(environment vector),以 NULL 结尾 |
cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[],char *env[])
{
int i = 0;
for(;env[i];i++)
{
printf("env[%d]:%s\n",i,env[i]);
}
printf("这是我自己实现的命令\n");
return 0;
}
运行结果:
本质是把环境变量表 传递给了进程 ,每个程序都会收到⼀张环境表 ,环境表是⼀个字符指针数组 ,每个指针指向⼀个以
'\0'结尾的环境字符串:
2.3.2 通过第三⽅变量environ获取
在C\C++标准库中,
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时 要⽤extern声明。
extern:在 C 和 C++ 中,extern是一个存储类说明符,用于声明变量或函数是在其他编译单元中定义的,当前文件只是"引用"它,而不是"定义"它,通俗讲就是告诉编译器:"这个符号在别处定义,链接时请去别处找。常用于声明外部全局变量"
| 关键字 | 链接属性 | 作用 |
|---|---|---|
extern |
外部链接 | 全局可见,可在其他文件使用 |
static(全局变量/函数) |
内部链接 | 仅本文件可见,隐藏实现 |
cpp
static int secret = 100; // 其他文件无法访问,除非包头文件
extern int shared = 200; // 其他文件可通过 extern 声明使用,不用包头文件!
cpp
int main()
{
extern char **environ;
int i = 0;
for(; environ[i]; i++)
{
printf("environ[%d]->%s\n", i, environ[i]);
}
return 0;
}
我们发现也能正常打印:
2.3.3 getenv(最常用的方法)

cpp
#include <stdlib.h>
char *getenv(const char *name);
原理就是遍历环境变量表 获取对应的环境变量 ,
getenv常来访问特定的环境变量。
cpp
int main()
{
char *whoami = getenv("USER");
printf("whoami: %s\n", whoami);
if(whoami == NULL)
{
printf("无法执行我,因为我不认识你!\n");
return 0;
}
else if(strcmp(whoami, "root") == 0)
{
printf("我也不准root执行!\n");
}
else if(strcmp(whoami, "fcy") == 0)
{
printf("你是合法的用户,我让你执行\n");
}
}
用
fcy账户执行:用
root执行:
2.4 环境变量的全局属性
我们所知的argv和env都是默认在bash内部的,而bash本身是个进程,所以这两张表本质是在内存中的两张临时的表!而众所周知 我的程序命令等,都是bash进程的子进程 !而子进程 是可以共享父进程 的代码和数据的(如果子进程 不改数据的话)。
所以我们可以这么理解:全局变量表 本质就是bash进程的数据!当我们运行自己的程序的时候,bash就会通过fork创建子进程,而环境变量 具有全局属性,此时
- 环境变量 不是某个函数或类的局部数据,而是属于整个进程;
- 无论你在
main()、某个工具函数、还是第三方库中调用getenv("PATH"),都能拿到相同的值。
是可以直接被⼦进程继承 下去(在未修改的情况下)!这就是哪怕我们不传char* env[]参数依然可以通过另外两个方法拿到全局变量的参数的原因!
2.4.1 bash进程的环境变量怎么来的
那么bash进程的环境变量又是从哪里来的呢?
是从
Linux的系统配置文件下获得的。
那么配置文件是从哪里获取的?
一般在家目录的
.bashrc中获取,然后通过.bashrc读取系统的配置文件(一般在/etc路径下)
2.4.2 export(创建环境变量)
export:可以将本地变量转换为全局变量,本质是把局部变量写到环境变量表里,属于内存级操作 ,当再次打开bash会重置!若想要永久存在,则需要修改系统的配置文件!本地变量:无法被子进程继承,不具备全局性,只能在bash内部被访问的变量。
2.4.3 unset(删除环境变量)
unset: 清除环境变量,将对应环境变量从表中删除,本质还是内存级,不影响配置文件。
2.4.4 set(查看环境和局部变量)
set: 显⽰本地定义的shell变量和环境变量。
2.4.5 echo怎么打印本地变量的?
- 按照上面的说法 我们的
echo应该也是bash的一个子进程,而子进程按道理来说是没办法继承局部变量的!但下图的echo依然可以打印局部变量。 - 我们还发现在修改
PATH的情况下,有些命令还依然能跑,这是为什么呢??
⚠️:这里可以得出个结论(目前了解这么多就行):命令和命令是不一样的!存在二进制文件级别的普通命令!是磁盘级,会加载成子进程然后执行 ; 同时也存在内建命令:在shell内部自己定义,不创建子进程,只是bash内部的的一次函数调用,不依赖第三方途径。


















