【Linux系统】命令行参数 和 环境变量(含内建命令介绍)

文章目录


一、命令行参数

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.环境变量的定义 以及 查看环境变量的方法

环境变量是系统级别的一些全局变量,每个环境变量都具备不同的用途。

  1. 通过 env 命令可以查看所有环境变量:
bash 复制代码
[zh@iZbp1dr1jtgcuih41mw88oZ ~]$ env


// 等号左边是环境变量名,右边是赋值

常见环境变量的作用:
• SHELL : 当前Shell(命令行解释器),它的值通常是/bin/bash
• USER : 登录时的用户
• PATH : 指定命令的搜索路径
• PWD : 当前工作路径
• OLDPWD : 上次所处路径
• HOME : 指定用户的家目录(即用户登陆到Linux系统中时,默认的目录)

  1. 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.进程获取环境变量的具体方法

  1. 通过main函数的 env 参数获取


  1. 通过 getenv函数 获取单个环境变量

指定环境变量名,getenv函数对照环境变量表查找该环境变量,找到就返回环境变量的赋值,没找到返回NULL


bash 复制代码
[zh@iZbp1dr1jtgcuih41mw88oZ test]$ ./myprocess
shell=/bin/bash
user=zh
  1. 通过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下的命令 分为两类:内建命令 和 外部命令(外部命令占绝大多数)

  1. 内建命令(Built-in Commands)
  • 定义:内置于 Shell 解释器(如 Bash)中的命令,无需加载外部可执行文件,直接由 Shell 进程执行。
  • 优势
    • 执行速度快(无磁盘 I/O 或子进程创建开销);
    • 可直接修改当前 Shell 环境(如 cd 改变工作目录、export 设置环境变量);
    • 几乎在所有 Linux 发行版中默认可用。
  • 常见命令cd, echo, pwd, source, exit, alias, export
  1. 外部命令(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

相关推荐
惊起白鸽4506 分钟前
LVS负载均衡
运维·负载均衡·lvs
Sapphire~1 小时前
Linux-07 ubuntu 的 chrome 启动不了
linux·chrome·ubuntu
伤不起bb2 小时前
NoSQL 之 Redis 配置与优化
linux·运维·数据库·redis·nosql
去旅行、在路上2 小时前
chrome使用手机调试触屏web
前端·chrome
广东数字化转型2 小时前
nginx怎么使用nginx-rtmp-module模块实现直播间功能
linux·运维·nginx
love530love2 小时前
【笔记】在 MSYS2(MINGW64)中正确安装 Rust
运维·开发语言·人工智能·windows·笔记·python·rust
啵啵学习2 小时前
Linux 里 su 和 sudo 命令这两个有什么不一样?
linux·运维·服务器·单片机·ubuntu·centos·嵌入式
半桔3 小时前
【Linux手册】冯诺依曼体系结构
linux·缓存·职场和发展·系统架构
网硕互联的小客服3 小时前
如何利用Elastic Stack(ELK)进行安全日志分析
linux·服务器·网络·安全
数字芯片实验室3 小时前
寄存器模型生成:从手工到自动化
运维·自动化