【Linux】环境变量、命令行参数
目录
环境变量
基本概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数如:我们在编写 C/C++ 代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
查看所有环境变量,输入 env (environment variables 的缩写)

进程的环境变量表在进程地址空间的内存布局

常见环境变量
- PATH : 指定命令的搜索路径
- HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL : 当前Shell,它的值通常是/bin/bash。
PATH
问题引入:为什么我们写的程序,要用 ./程序名 来运行,而像 ls、pwd 这样的指令,直接输入名字就可以运行?
原因是:在运行 xshell 时,有一些环境变量就事先被创建了,比如 PATH(Linux 指令搜索路径)
如何在 Linux 下查看PATH的内容:
这个变量存储了多个路径,用 : 作为分割符。这些路径,就是系统查找我们输入的路径的默认查找路径。我们写的程序不在上面的任何一条路径,所以不能直接输入写的程序的程序名来运行程序。
如果要把我们写的程序也像 ls 指令那样直接输入名字就能执行,可以把程序所在的路径也加入到 PATH 环境变量:
注意:
1、等号两侧不能有空格
2、PATH=/home/hxh 会覆盖PATH之前的内容
3、退出重新登陆xshell,PATH 会恢复。
HOME
问题引入:为什么 root 用户登陆时,处于 /root 目录,而普通用户登陆时处于 /home/用户名 ?
原因是:登陆 Linux 时,shell 会识别登陆的用户,据此填充 HOME 环境变量
PWD
如果进程想要创建临时文件,那么创建在哪里呢?以 touch 指令为例,touch 指令创建的文件都是在当前目录下创建的,为什么 touch 指令记得要在当前目录下创建呢,原因就是 touch 指令的 task_struct 记下了当前目录(cwd),fopen 函数同理。注意 cwd 是操作系统内核维护的变量,存储位置在操作系统内核的数据结构,而 PWD 是环境变量,存储在进程的环境变量空间,由 shell/程序手动设置,是给用户和程序的参考。
系统调用:getenv
我们不仅在命令行可以用 echo $环境变量名 获取环境变量的内容,也可以在程序中用 getenv 函数获取:
要包含头文件 #include <stdlib.h>
命令行参数表和环境变量表
1、命令行参数表
main 函数也可以有参数:int main(int argc, char *argv[]),argc 是 argv(指向字符串的指针的指针数组)指向的字符串数组的有效字符串个数,argv 最后一个有效指针的下一个一定是 NULL。
编译运行:
其实,向 bash 命令行输入的,都是一个字符串,这些字符串,以空格为分割符,分别存储在 bash 进程的 argv 数组里。bash 会对输入的字符串进行解析,把一个字符串分割为几个字符串,比如向命令行输入 "ls -a -l“,bash 会解析成 "ls","-a","-l"储存在 argv 数组里,然后创建子进程,把 argv 数组传递给子进程,告诉子进程该做什么(也可能不创建子进程,bash 亲自执行,即内建指令,详情见下)。这样做可以实现指令、工具、软件带不同的参数可以实现不同的功能(支持命令行选项)。
2、环境变量表
main 函数的参数其实还有一个:int main(int argc, char* argv[], char* env[]),即:两张核心的向量表:命令行参数表 argv 和环境变量表 env。我们所运行的进程,都是 bach 进程的子进程,bach 在启动时,会从操作系统的配置文件中读取环境变量信息(用户目录下的 .bash_profile 文件,里面保存了导入环境变量的方式),子进程会继承父进程交给我们的环境变量(环境变量存储在进程的进程地址空间中,位于栈区上面)。子进程如果再创建子进程,子进程的子进程又会继承子进程的环境变量,这就是为什么说环境变量具有全局性。
通过全局变量 environ 获取环境变量
libc 中定义的全局变量 environ 指向环境变量表, environ 没有包含在任何头文件中,所以在使用时 要用 extern 声明。
如何验证“子进程会继承父进程交给我们的环境变量”
要验证这个问题,首先要学会如何创建环境变量:
创建环境变量的方式:export 环境变量名=初始值(=两侧不能有空格)
在 bach 命令行创建的环境变量,与任何子进程无关,如果要求一个子进程打印环境变量,发现打印的环境变量有 MY_VALUE,就证明了这个问题。
可以发现打印的环境变量有 MY_VALUE:
创建环境变量还可以使用 putenv 函数,如果子进程想要新增一个环境变量,但不想通过 bash 用 export 后通过继承的方式新增,就可以在程序内调用该函数,调用该函数的程序就可以新增一个环境变量,而该程序的父进程的环境变量表没有该环境变量。
#include <stdlib.h> #include <stdio.h> int main() { putenv("MY_ENV=123"); printf("%s\n",getenv("MY_ENV")); return 0; }
删除环境变量:unset 环境变量名
补充知识:本地变量和内建命令
本地变量
上面创建的变量是环境变量,在创建时声明 export,如果不声明 export,直接用 变量名=初始值(=两侧不能有空格)方式创建的变量,叫做本地变量
使用 set 指令可以查看系统定义的所有变量,包括环境变量和本地变量
本地变量不会被子进程继承,只在本 bach 内部有效。
本地变量和环境变量可以相互转化:把本地变量变成环境变量的方式:export 本地变量名,把环境变量变成本地变量的方式:unset 环境变量名
内建命令
问题引入:
不是说好的 bach 都是创建子进程,让子进程去执行指令,自己继续继续命令行解释吗?不是说本地变量不会被子进程继承,那现在子进程打印出了本地变量,怎么解释?
原因是:
bach 指令可以分成两种:
1、常规指令:通过创建子进程完成
2、内建指令:bach 不创建子进程,而是自己亲自执行,本质是 bach 调用自己写的函数,或者系统提供的函数。(常见内建指令:cd、pwd、echo),所有需要修改当前 shell 进程状态的操作(包括环境变量、工作目录、shell 选项、别名等)都必须是内建指令,否则无法生效。
更多推荐




















所有评论(0)