文章目录
- 一、命令行参数
- 二、环境变量
-
- [1.环境变量的定义 以及 查看环境变量的方法](#1.环境变量的定义 以及 查看环境变量的方法)
- 2.环境变量是如何获取的
- 3.进程获取环境变量的具体方法
- [4.本地变量 和 环境变量的区别](#4.本地变量 和 环境变量的区别)
- [5.内建命令 和 外部命令](#5.内建命令 和 外部命令)
-
- [5.1 内建命令 和 外部命令的区别](#5.1 内建命令 和 外部命令的区别)
- [5.2 怎么证明 echo 和 cd 等命令是内建命令](#5.2 怎么证明 echo 和 cd 等命令是内建命令)
- 6.补充:环境变量PATH的用途
一、命令行参数
main函数有参数吗?
答:main函数一共有三个参数:int argc、char * argv[ ] 和 char * env[ ],
前两个参数是和命令行参数有关的(argc记录命令行参数个数,每个命令行参数都是一个字符串,argv保存每个命令行参数的地址),最后一个和环境变量有关。这些参数在不使用时可以不加。
使用示例:

bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ll
total 8
-rw-rw-r-- 1 zh zh 138 May 26 23:33 makefile
-rw-rw-r-- 1 zh zh 204 Jun 2 20:51 process.c
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ make
gcc -c process.c -g
gcc -o myprocess process.o
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ll
total 24
-rw-rw-r-- 1 zh zh 138 May 26 23:33 makefile
-rwxrwxr-x 1 zh zh 9512 Jun 2 20:52 myprocess
-rw-rw-r-- 1 zh zh 204 Jun 2 20:51 process.c
-rw-rw-r-- 1 zh zh 3560 Jun 2 20:52 process.o
现象如下:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess
argv[0]:./myprocess
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess -i
argv[0]:./myprocess
argv[1]:-i
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess -i -b -a
argv[0]:./myprocess
argv[1]:-i
argv[2]:-b
argv[3]:-a
我们在命令行输入的命令本质是字符串,跟据字符串中的空格又可以把完整的字符串分成一个一个的子串(每一个子串就是一个命令行参数),参数argc中记录了子串的个数,参数argv中按顺序保存了每一个子串。
使用参数argc 和 argv 记录命令行参数有什么作用呢?
示例展示其作用:

bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess
执行基础操作
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess -a
执行基础操作+A操作
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess -b
执行基础操作+B操作
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess -c
执行基础操作+C操作
当我们自己写的 myprocess可执行文件 中的main函数可以接收到命令行参数,就可以编辑代码实现接收到不同的命令行参数,让可执行程序执行不同的操作。
Linux下有很多系统命令,它们在使用时会提供很多选项(比如:ls命令提供了-l、-a、-d等选项),使用不同选项就能够实现不同的功能效果。
原理解析: 系统命令大多数都是可执行程序,它们的main函数通过argc 和 argv参数获取到 命令行参数的信息,解析命令所带的选项,不同的选项就可以执行不同的功能。
命令行 执行的可执行程序,它们的main函数的参数是如何获取到命令行参数信息的呢?
答:通过它们的父进程-bash。-bash进程是由系统创建的,当系统启动时就会创建好-bash进程。
-bash进程其实就是Linux系统下的命令行解释器,我们在命令行执行的命令/程序形成的进程都是由-bash进程创建的子进程。
当我们在命令行中输入:./myprocess -i -b -a 等信息时,是命令行解释器-bash进程先接收到这些信息,将命令行参数保存到命令行参数表 (实际就是-bash进程中维护的一个字符串指针数组)中:
然后-bash进程创建 子进程myprocess,子进程创建时会共享父进程的代码和数据,所以子进程就也会获取到命令行参数信息。
二、环境变量
1.环境变量的定义 以及 查看环境变量的方法
环境变量是系统级别的一些全局变量,每个环境变量都具备不同的用途。
- 通过 env 命令可以查看所有环境变量:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ ~]$ env
// 等号左边是环境变量名,右边是赋值
常见环境变量的作用:
• SHELL : 当前Shell(命令行解释器),它的值通常是/bin/bash
• USER : 登录时的用户
• PATH : 指定命令的搜索路径
• PWD : 当前工作路径
• OLDPWD : 上次所处路径
• HOME : 指定用户的家目录(即用户登陆到Linux系统中时,默认的目录)
- echo $NAME (NAME:你的环境变量名称)
echo 指定某个环境变量名 来查看 该环境变量赋值
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ echo $SHELL
/bin/bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zh/.local/bin:/home/zh/bin
2.环境变量是如何获取的
先说结论:所有进程在创建时都会获取到环境变量的信息,所以环境变量才被称为系统级别的全局变量
这是因为-bash进程中保存了环境变量的信息,我们在命令行执行的命令/程序形成的进程都是由-bash进程创建的子进程,子进程创建时会共享父进程的代码和数据,所以子进程就也会获取到环境变量的信息,这些-bash进程创建的子进程中再去创建它们的子进程(相当于"孙子进程")时,孙子进程在创建时又会共享子进程中保存的环境变量信息。最终,所有进程在创建时都获取到了环境变量信息。
那么 -bash进程 是如何获取环境变量的信息的呢?
从系统配置文件(系统全局配置文件/etc/profile,用户级配置文件~/.bash_profile)中获取 ,所有用户登录时先加载全局配置文件,为所有用户提供默认设置;紧接着指定用户再加载自己家目录下的用户级配置文件。
我们不用管具体的加载过程,简单一些来理解就是:系统配置文件中保存了所有环境变量的初始信息,-bash进程实际上就是一个可执行程序(底层由C语言代码实现),所以-bash进程启动时,可以通过读文件操作读取系统配置文件中保存的环境变量信息,它把获取到的所有环境变量信息保存在环境变量表中(实际就是一个字符串指针数组),方便子进程获取:

3.进程获取环境变量的具体方法
- 通过main函数的 env 参数获取
- 通过 getenv函数 获取单个环境变量
指定环境变量名,getenv函数对照环境变量表查找该环境变量,找到就返回环境变量的赋值,没找到返回NULL
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess
shell=/bin/bash
user=zh
- 通过C标准库中定义的全局变量environ获取
C标准库中定义的全局变量environ的类型是char ** ,它指向环境变量表开头,可以通过environ循环遍历环境变量表。
使用全局变量environ无需包含额外头文件,用extern声明(extern char ** environ)之后即可使用
4.本地变量 和 环境变量的区别
本地变量 就是我们在命令行临时创建的变量(在环境变量表中是查不到它的):
bash
[zh@iZbp1dr1jtgcuih41mw88oZ ~]$ ONE_PIECE="Luffy"
[zh@iZbp1dr1jtgcuih41mw88oZ ~]$ echo $ONE_PIECE
Luffy
[zh@iZbp1dr1jtgcuih41mw88oZ ~]$ env | grep ONE_PIECE
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ set | grep ONE_PIECE
ONE_PIECE=Luffy
// set命令: 显示本地定义的shell变量和环境变量;我们在通过set命令是可以查到我们自己定义的本地变量的
本地变量只在-bash进程中有效,-bash进程创建的子进程是无法继承本地变量的:

bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess
不存在ONE_PIECE变量
作一下对照试验,我们可以使用export命令(export命令: 设置⼀个新的环境变量)把本地变量设置成环境变量:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ export ONE_PIECE
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ env | grep ONE_PIECE
ONE_PIECE=Luffy
把本地变量ONE_PIECE设置成环境变量之后,子进程就可以继承了:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess
one_piece:Luffy
// unset 变量名: 清除指定的环境变量 或 本地变量(系统设置为只读属性的变量是无法删除的,如环境变量PWD 和 SHELL等);一般用于:自己export设置的环境变量不想使用了,就用unset命令把它删除。
结论:在shell命令行创建的本地变量,只在-bash进程中有效,-bash进程创建的子进程是无法继承本地变量的。
而环境变量是具有全局属性的,-bash进程创建的子进程会继承环境变量的信息,-bash进程创建的子进程中再去创建它们的子进程(相当于"孙子进程"),孙子进程在创建时又会继承子进程中保存的环境变量信息。最终,所有进程在创建时都可以获取到环境变量信息。
补充:
-bash 进程中保存了以下数据结构(部分):
- 环境变量表 (environ)
存储环境变量(全局可见性,可被子进程继承) - -bash 变量表
存储本地变量(仅当前-bash进程中可见,不能被子进程继承)
5.内建命令 和 外部命令
5.1 内建命令 和 外部命令的区别
Linux下的命令 分为两类:内建命令 和 外部命令(外部命令占绝大多数)
- 内建命令(Built-in Commands)
- 定义:内置于 Shell 解释器(如 Bash)中的命令,无需加载外部可执行文件,直接由 Shell 进程执行。
- 优势 :
- 执行速度快(无磁盘 I/O 或子进程创建开销);
- 可直接修改当前 Shell 环境(如
cd
改变工作目录、export
设置环境变量); - 几乎在所有 Linux 发行版中默认可用。
- 常见命令 :
cd
,echo
,pwd
,source
,exit
,alias
,export
。
- 外部命令(External Commands)
- 定义 :独立的可执行文件,存储在
/bin
、/usr/bin
等目录中,执行时需创建子进程。 - 特点 :
- 执行速度慢,每次执行需加载磁盘文件;
- 可被多个 Shell 共享,但执行效率较低;
- 常见命令 :
ls
,grep
,ps
,cat
。
区分方法
bash
# 使用 `type` 命令检测命令类型
type cd # 输出: cd is a shell builtin(内置命令)
type ls # 输出: ls is /usr/bin/ls(外部命令)
# 使用 `help` 仅显示内置命令的帮助
help cd # 显示帮助信息
help ls # 报错:ls: not a shell builtin
为什么某些命令(cd 、echo等)同时有内建和外部版本?
- 示例 :
echo
既是 Bash 内建命令,又有外部程序/bin/echo
。 - 原因 :
- 内建版:优化常用操作(如快速输出);
- 外部版:兼容其他 Shell 或特殊场景(如通过
find -exec
调用)。
- 调用优先级 :内建命令优先于外部命令(除非使用绝对路径,如
/bin/echo
)。
总结表:关键区别
特性 | 内建命令 | 外部命令 |
---|---|---|
执行位置 | 当前 Shell 进程(-bash进程) | 单独子进程 |
速度 | 快(无 I/O 开销) | 慢(需加载可执行文件) |
环境修改能力 | 可修改当前 Shell 环境 | 无法修改父进程环境 |
依赖路径 | 不依赖 PATH 变量 | 依赖 PATH 查找 |
检测命令 | type 命令名 |
which 命令名 |
5.2 怎么证明 echo 和 cd 等命令是内建命令
在shell命令行创建的本地变量仅在当前-bash进程中可见,不能被子进程继承。假设echo命令是一个外部命令,外部命令就是可执行文件,执行时需创建子进程,那么这就意味着echo命令是不能访问本地变量的,但实际情况如下:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ONE_PIECE="Luffy"
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ env | grep ONE_PIECE
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ set | grep ONE_PIECE
ONE_PIECE=Luffy
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ echo $ONE_PIECE
Luffy
我们在shell命令行创建的本地变量ONE_PIECE,echo命令竟然能直接访问,所有这反向证明了echo命令肯定不是一个外部命令,而是内建命令。
-bash进程中维护的 本地变量表仅在当前-bash进程中可见。内建命令 是内置于 命令行解释器(如 -bash,linux下的命令行解释器就是-bash)中的命令,无需加载外部可执行文件,无需创建新进程,直接由 -bash 进程执行,所以内建命令可以访问 本地变量表(因为内建命令是直接运行在 -bash 进程的内存空间中)
cd
命令示例分析
bash
$ cd /tmp # 切换到 /tmp 目录
$ pwd # 输出: /tmp
- 为什么cd命令必须是内建命令?
若cd
是外部命令,每次执行会创建子进程并在子进程中切换目录,进程之间是相互独立的,子进程中的修改是无法影响父进程 -bash的目录状态的。而 cd命令预期要达到的效果就是切换-bash进程的当前工作目录,所以 cd命令只能是内建命令,只有内建命令能直接修改 -bash进程环境。
内建命令 cd 的效果实现:
首先,-bash进程的pcb中存储了它的当前工作目录(pcb中的cwd变量),
所以,要修改 -bash进程的当前工作目录,实际就是修改pcb中的cwd变量中保存的内容,
直接调用系统函数 chdir() 就可以修改 -bash进程的当前工作目录,
修改 Bash 进程的当前工作目录成功后,还会同步更新 PWD 环境变量(环境变量PWD中记录的也是-bash进程的当前工作目录)
// OLDPWD环境变量存储上一次的工作目录路径,由-bash进程自动维护,用于cd -快速返回上一目录。它的更新机制也与cd命令有关,当cd命令要切换目录(修改当前工作目录)时,已经检测到要切换的新目录是有效的,先更新OLDPWD环境变量,当前目录路径(PWD变量)在切换前被立即复制到OLDPWD,然后再更新 PWD环境变量。
系统调用函数 chdir() :
(1)作用:修改执行该函数的进程的当前工作目录,也就是修改进程pcb中cwd变量的内容。
(2)用法:chdir("/newpath"),在chdir中指定新的工作目录路径,如果这个新路径存在的话,该进程的当前工作路径就会被修改为指定的新工作目录
(3)返回值:修改成功,返回0;修改失败,返回-1
6.补充:环境变量PATH的用途

我们自己写的可执行程序必须要指定绝对路径 或 相对路径才能够顺利执行,不带路径直接写该程序名会报 -bash进程没有找到该命令的错误:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ make
gcc -c process.c -g
gcc -o myprocess process.o
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ll
total 24
-rw-rw-r-- 1 zh zh 138 May 26 23:33 makefile
-rwxrwxr-x 1 zh zh 9360 Jun 4 13:06 myprocess
-rw-rw-r-- 1 zh zh 105 Jun 4 13:06 process.c
-rw-rw-r-- 1 zh zh 3280 Jun 4 13:06 process.o
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess
hello world
one piece
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ /home/zh/test/myprocess
hello world
one piece
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ myprocess
-bash: myprocess: command not found
前面我们讲了Linux下的命令分为两类:内建命令 和 外部命令,而外部命令其实是占绝大多数。
外部命令的本质就是可执行文件,存储在 /bin
、/usr/bin
等目录中,执行时需创建子进程。
以外部命令 ls为例:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ /usr/bin/ls
makefile myprocess process.c process.o
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ls
makefile myprocess process.c process.o
我们发现外部命令竟然不指定路径也可以直接运行,这是为什么呢?外部命令不也是可执行程序吗?
答:环境变量PATH记录了外部命令(可执行文件)的搜索路径。当我们在命令行中输入不带路径的外部命令时,-bash进程会默认在PATH变量保存的搜索路径下寻找,找到了会自动给该命令添加上路径。 所以只要外部命令(可执行文件)的搜索路径被记录PATH变量中,它就可以不指定路径 直接运行。
可以查看 环境变量PATH的内容(每个搜索路径以:隔开)来验证,发现果然有/usr/bin搜索路径,而外部命令 ls就是保存在该路径下的:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zh/.local/bin:/home/zh/bin
进一步验证,我们把自己写的可执行程序所处路径添加进PATH变量中,测试该可执行程序是否也可以不指定路径 直接运行:
(命令行解释器-bash进程中保存着环境变量表和本地变量表,在命令行可以使用变量赋值的方式修改已有的环境变量 或 本地变量;若变量赋值时,该变量不存在,则新建一个本地变量,变量赋值前加了export的话,就是新建一个环境变量)
注: 被系统设置为只读属性的变量(无论环境变量 还是 本地变量)无法用变量赋值的方式修改
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ll
total 24
-rw-rw-r-- 1 zh zh 138 May 26 23:33 makefile
-rwxrwxr-x 1 zh zh 9360 Jun 4 13:06 myprocess
-rw-rw-r-- 1 zh zh 105 Jun 4 13:06 process.c
-rw-rw-r-- 1 zh zh 3280 Jun 4 13:06 process.o
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ pwd
/home/zh/test
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ PATH=$PATH:/home/zh/test
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zh/.local/bin:/home/zh/bin:/home/zh/test
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ myprocess
hello world
one piece
我们通过变量赋值的方式把自己写的可执行程序所处路径添加进PATH变量中,发现该可执行程序果然就可以不指定路径 直接运行。
做最后一步验证,把PATH变量的内容清空,测试 ls 等外部命令是否就无法不指定路径 直接运行:
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ PATH=""
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ echo $PATH
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ls
-bash: ls: No such file or directory
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ cat ./process.c
-bash: cat: No such file or directory
当PATH变量的内容被清空,也就是外部命令(可执行文件)的搜索路径被清空,所以 外部命令不指定路径 不能直接运行了,因为-bash进程不知道去哪个路径下搜索这些外部命令。
补充:
当把PATH变量的内容清空后,内建命令不受任何影响,依然可以直接运行。
这是因为内建命令是内置于 命令行解释器Bash中的命令,无需加载外部可执行文件,内建命令在执行时会完全跳过 PATH 变量的路径查询,直接由 Shell 进程执行。
bash
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ echo $PATH
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ pwd
/home/zh/test
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ cd ..
[zh@iZbp1dr1jtgcuih41mw88oZ ~]$ echo $USER
zh