首先要知道几个事实:
Linux
里面使用的命令行指令,也是经过代码编写产生的可执行文件。而在Linux
下的任何文件路径都可以使用这些指令,那能不能让我们自己编写的代码也这样呢?答案是可以,不过需要使用环境变量。C/C++
代码的时候,我们是不知道链接时期需要的的动态静态库在哪里的,都是照样可以链接成功,生成可执行程序,原因也是有相关环境变量帮助编译器进行查看。环境变量是指在操作系统中用来指定操作系统运行环境的一些参数(类似语言的全局变量),环境变量通常具有某些特殊用途,在系统中通常具有全局的特性。
下面是一些常见的环境变量:
PATH
:指定命令的搜索路径
HOME
:指定用户的主工作目录(指令cd ~
就是根据HOME
变量来进行确认的)
SHELL
:当前Shell
,其值通常为/bin/bash
。
HISTSIZE
:其值为设置shell历史记录大小的变量(使用history
指令可以查看HISTSIZE
条历史指令)
LS_COLORS
:环境变量是用于设置ls
命令在终端中显示不同文件类型和属性时的颜色的变量。通过指定不同文件类型和属性对应的颜色码,可以使文件在终端中以不同的颜色进行区分和显示
LOGNAME
:记录登录的用户名
PWD
:保存当前的路径
Linux
中的环境变量有很多,基本都是独立工作的,各有各的功能(在Windows
操作系统里也是有环境变量的)。
使用命令echo $NAME
即可查看对应变量的情况,注意需要带上$
符号。
$ which pwd
/usr/bin/pwd
$ echo $PATH
...:/usr/bin:... #这里可以找到pwd可执行文件的所在地,还有其他的地址,使用“:”进行分割
还可以使用命令env
来查看当前系统的所有环境变量,这里输出结果有很多,可以看到所有的环境变量及其值(无法查看shell
环境变量)
set
命令则是显示本地定义的shell
变量和环境变量(关于shell
变量后面有提及)
$ limou=123456789
$ set > text
$ cat text | grep limou
limou=123456789
如果我们想要实现我们自己编写的可执行程序和Linux
内置指令一样的使用效果,有两种方法:
把您自己写的可执行程序放进PATH
环境变量中的文件路径下,这样就可以像使用Linux
指令一样使用自己编写的可执行程序(一般放在/usr/bin
下比较多,这种行为叫做“给系统安装程序”,但是这种做法不建议,有可能污染操作系统)。
还有一种方法是使用命令export <环境变量>=$<环境变量>:新值
配置PATH
环境变量,这样操作不会污染到系统的文件。如果使用命令unset 某环境变量
可以清除某个环境变量,如果使用unset PATH
后就会发现其他的Linux
指令都无法使用了,不过不用担心,下次登录还是会恢复默认的PATH
值的,这是因为系统再重新登录的时候,会重新读取保存环境变量的配置文件(也就是家目录下的.bash_profile
文件),而父进程bash
会重新加载环境变量。
另外,可以使用export
指令将已经存在的shell
变量转变为环境变量,只需要使用export <已经存在的shell变量>
即可。
补充
1
:实际上对应环境变量这样的“系统变量”,还有一个“本地变量”,即:shell
变量,就是直接在bash
定义一个变量,这个变量的值可以使用echo $
指令查看,但不能使用env
命令查看,因为它还不是环境变量,其作用域只在bash
父进程中,还没有被其他子进程继承。补充
2
:不过,既然不能将shell
变量继承给子进程,那么在父进程下运行的echo
不也是bash
的子进程么,它是怎么读取到我们设置的shell
变量的呢?如果我们使用PATH=""
把PATH
清空,就会惊奇发现,其他大部分命令都失效了,但是echo
命令依旧可以正常打印shell
变量。$ limou=123456789 $ echo $limou 123456789 $ PATH="" $ echo $limou 123456789 $ echo $PATH $ echo $HOME /home/ljp $ echo $SHELL /bin/bash $ ls bash: ls: No such file or directory $ touch bash: touch: No such file or directory
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这是因为在
Linux
中,大部分的常规命令都是磁盘上真实存在,并且需要由fork()
创建子进程来执行的,但是还有一些命令不需要创建子进程来执行,这种命令由bash
自己来执行,也叫做bash
的内建命令。因此echo
命令不会创建子进程,直接在bash
内执行,当然可以获取bash
自己读取的到的变量(环境变量和shell
变量)。这样的内建命令有很多,包括pwd
、export
等等。
$ limou=1234567
$ echo $limou
1234567
$ env | grep $limou
$ export limou
$ env | grep $limou
limou=1234567
上述环境变量添加操作实际上是加载到内存中,而不是加载到环境变量配置文件中。
直接将环境变量的名字和值以export <环境变量名>=<环境变量值>
的形式写入.bash_profile
则可以达到登录即可使用自定义环境变量的目的,但是也有污染的可能。
注意:一般情况下,在
Linux
命令行中修改环境变量只适用于本次会话(本次登录),一旦退出登录就无效了,不过如果修改的是保存环境变量的文件就会永久有效。
环境变量通常使用环境变量表组织起来,而环境表是一个字符指针数组,每个指针指向一个以\0
结尾的环境变量字符串,并且最后一个元素指向NULL
表示结尾,这样做就可以将所有环境变量组织起来。
因此我们编写代码的时候可以使用以下的main()
接口来接受和操作运行程序时使用的选项和环境变量:
#include
#include
int main(int argc, char* argv[], char* env[])
{
if(strcmp(argv[argc - 1], "-order") == 0)
{
printf("---------\n");
for(int i = 0; env[i]; i++)
{
printf("%s\n", env[i]);
}
printf("---------\n");
}
return 0;
}
这里的env
数组也就对应上面提到的由环境变量构成的char*
类型数组,该数组视NULL
的结尾。
上面的mian()
程序被系统启动后,接受了两张表,一是命令行参数表,二是环境变量表
在上面的代码中,main
函数可以带上三个参数,第三参数实际上就是有关环境变量的参数(前两个数选项个数和选项字符数组):
int mian(int argc, char * argv[], char * env[]) {}
除了这种方法还可以使用unistd.h
内定义的environ[]
全局数组来获取环境变量的值。
但是最常用的是使用stdlib.h
的getenv()
函数,可以根据参数来获取对应的环境变量值(其参数是想获取环境变量的变量名字符串)。
还有一些其他有关环境变量的接口,您可以自行探索一下。
补充
1
:一般main()
的环境变量参数是由父进程继承过来的,父进程也是从它自己的父进程获取的,因此环境变量最终在bash
进程获取,而所有进程的父进程是当前bash
。这也就是环境变量之所以具有全局属性的本质原因,而env[]
参数也是依靠父进程传参的。这种父子传递关系,您可以在
bash
下自己创建一个自定义的环境变量,然后使用C
语言库内的getenv()
函数读取环境变量来验证。欸,但是父进程的环境变量又是那里来的?只能是操作系统读取配置文件了。
补充
2
:shell
变量没有办法在C
代码中使用getenv()
获取。
shell
变量实际上最常用的场景是在shell
脚本里,这个shell
脚本我们以后再提及。