前言
在前面我们学习了进程状态、创建进程、进程调度等等,本文我们继续学习进程概念——环境变量。
一、 常见环境变量
- PATH:指定命令的搜索路径
- HOME:指定用户的主工作目录(即用户登录到Linux系统中时,默认所处的目录)
- USER:当前用户是谁
- LOGNAME:当前登录用户是谁
- PWD:当前进程所处路径
- OLDPWD:上一次进程所处路径
- HOSTNAME:当前机器的主机名
- HISTSIZE:历史命令保存的条数(history指令可以查看历史上的所有指令)
- LS_COLORS:ls配色方案
- SHELL:当前shell,它的值通常是/bin/bash
- SSH_TTY:当前终端设备文件
1. PATH
问题1:我们自己写的程序编译成为可执行程序后也是指令,为什么自己可执行程序在运行的时候要带【./】,而系统指令不需要?
- 首先我们都知道要执行任何一个程序,都必须要找到这个程序对应的二进程文件!
- 系统指令不需要指定其对应的二进程文件就可以运行,是因为Linux系统帮系统指令提供了一个PATH环境变量,PATH中存着系统指令的搜索路径。在执行指令的时候如果没有指定搜索路径,系统会自动帮我们去PATH中一个路径一个路径的搜索。
- 我们自己的程序不在系统默认的搜索路径上,所以我们需要指定路径【./】
问题2:怎么查看(读取)PATH中的搜索路径?
echo $环境变量
- 注意:不能echo直接打印,因为我们要查看的是PATH的内容
- PATH的内容是一堆路径,使用【:】为分隔符
问题3:PATH中可以再添加路径吗?如何添加?
- 注意:PATH=路径(等号两侧不能有空格),直接添加路径,会将之前的PATH内容覆盖。(如何恢复?——》关掉Shell,重新登录)
- PATH=$PATH:路径,新增,不覆盖。
小结:
- 执行任何一个程序,都必须要找到这个程序对应的二进程文件!
- 为什么在执行系统指令的时候可以不用带路径?Shell怎么知道我要执行的指令在哪呢?——》因为在系统中,Shell会维护一个PATH环境变量,PATH指定了系统指令的搜索路径
- which在搜索搜索指令路径时,在哪搜索?——》根据PATH环境变量来搜索指令路径的
2. HOME
问题:为什么首次登录shell时,默认所处目录是在自己的家目录
- HOME:指定用户的主工作目录(即用户登录到Linux系统时,默认所处的目录)
- 登录shell时,shell会识别你这个用户是谁,给你填充$HOME环境变量,并默认帮你cd $HOME
3. SHELL
问题:我们系统中,我怎么知道我当前用的是哪一个shell呢?
- SHELL:当前shell,它的值通常是/bin/bash。
4. 查看环境变量
使用指令查看环境变量,有两种方式:
- 使用echo指令,指定查看某一个环境变量
echo $环境变量名
- 使用env指令,查看所有的环境变量
env
使用系统调用接口:getenv
- 头文件:stdlib.h
- 函数声明:cher* getenv(const char *name);
- 作用:在环境变量列表中搜索指定的 name,并返回对应值的字符串指针,即获取指定一个环境变量。
#include<stdio.h> #include<stdlib.h> #include<string.h> int main() { char who[32]; strcpy(who, getenv("USER")); if(strcmp(who, "root") == 0) { printf("让他做任何事情\n"); } else { printf("你就是一个普通用户,受权限约束!\n"); } // printf("PATH: %s\n", getenv("PATH")); // printf("whoami: %s\n", getenv("USER")); return 0; }
- 有环境变量USER系统就可以认识你是谁,认识你是谁就可以和文件属性中的拥有者,所属组权限作对比,就可以判断你的权限
二、什么是环境变量
1. 概念
环境变量是系统提供的一组name=value形式的变量,不同的环境变量有不同的用途,通常具有全局属性
2. 环境变量的组织方式
每一个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以‘\0’结尾的环境字符串。
3. main的三个参数
main函数其实是有三个参数,以前因为我们写代码时没用命令行,所以不用带参。
main函数的三个参数:int main(int agrc, char* argv[], char* env[]) { return 0; }
- argc:表示argv指针数组的大小
- argv:指向命令行参数表
- env:指向环境变量表
3.1 命令行参数表
argv:指向命令行参数表
#include<stdio.h> #include<stdlib.h> #include<string.h> int main(int argc, char *argv[]) { int i = 0; for(; i < argc; i++) { printf("argv[%d]->%s\n", i , argv[i]); } return 0; }
- 我们在bash输入的指令,就是命令行参数
- 命令行参数在bash看来你输入的是一个字符串,在做命令行解释的时候,它会将这个大字符串以空格作为分隔符打散为一个一个子串,然后有几个子串就初始化argc为几,每个子串的起始地址按照顺序保存到argv,最后传给main函数——》将命令行参数打散并传给main的工作是bash做的,这是指令的解析工作
- 命令行参数第一个是命令,其他的都是选项
- 命令行参数表的最后指向NULL
- 为什么bash要做指令的解析工作呢?
#include<stdio.h> #include<stdlib.h> #include<string.h> int main(int argc, char *argv[]) { if(argc != 2) { printf("Usage: %s -[a|b|c]\n", argv[0]); return 0; } if(strcmp(argv[1], "-a") == 0) { printf("功能1\n"); } else if(strcmp(argv[1], "-b") == 0) { printf("功能2\n"); } else if(strcmp(argv[1], "-c") == 0) { printf("功能3\n"); } else { printf("default功能\n"); } return 0; }
- 因为命令行参数为指令、工具、软件等提供命令行选项的支持!!!(不同的选项,提供不同的功能)
3.2 环境变量表
env:指向环境变量表
- 环境变量表的结构与命令行参数表的结构一模一样,最后也指向NULL。
#include<stdio.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]); } return 0; }
- C/C++程序,有两张核心向量表:一、命令行参数表;二、环境变量表。
- 所以我们一个进程在运行时,不要简单认为进程启动就是把程序加载到内存,而是在程序变成进程启动时,bash调用main函数,给main传了两张核心向量表。
- 如上图我们发现打印的环境变量表和shell的一样,为什么?
- 我们自己的程序本身是没有环境变量的,但是当它启动之后,变成进程就有了环境变量。
- 这是因为我们的进程是bash的子进程,子进程可以继承父进程环境变量的信息
- 我们所运行的进程,都是bash的子进程,bash本身在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程的环境变量!
- 子进程会继承父进程的环境变量——》环境变量具有全局性
验证:子进程会继承父进程的环境变量
- 给bash新增一个环境变量,看是否会被继承
注意:在命令行直接定义的变量,不是环境变量,是本地变量
- 本地变量,我们在环境变量表中是查不到的
如何设置一个环境变量?
- export:设置一个新的环境变量
重新运行我们刚才打印环境变量的程序,确实子进程会继承父进程的环境变量
如何清除环境变量*
- unset:清除环境变量
我们除了通过main函数的参数获取环境变量,还可以通过第三方变量environ获取
#include<stdio.h> int main() { extern char **environ; int i = 0; for(; environ[i]; i++) { printf("environ[%d]->%s\n", i , environ[i]); } return 0; }
- libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
4. 和环境变量相关的命令
- echo: 显示某个环境变量值
- export: 设置一个新的环境变量
- env: 显示所有环境变量
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
5. 本地变量 && 内建命令
5.1 本地变量
本地变量:在命令行直接定义的变量就是本地变量
注意:本地变量不是环境变量,在环境变量表中是查不到的!
本地变量的查看有两种方式:
- 使用echo查看
echo $本地变量名
- 使用set查看
# set查看所有系统变量,包括本地变量和环境变量 set # 使用管道和grep过滤,查看指定变量 set | grep 指定变量名
- 注意:本地变量是不会被子进程继承的,他只会在本bash内部有效!
- 本地变量的使用,例如:命令行的前缀,root是#,普通用户是$。
- export:可以将本地变量导成环境变量
5.2 内建命令
在之前的学习中,我们说命令行当中执行的所有指令,都是bash的子进程。那现在就有一个问题,我们定义了一个本地变量MY_VALUE,使用指令echo可以查看,但不是说本地变量不继承吗?
echo要不要创建子进程?——》命令行所运行的指令,不一定都要创建子进程——》命令行中的命令,分为两批命令
两批命令:
- 常规命令:通过创建子进程完成的
- 内建命令:bash不创建子进程,而是由自己亲自执行,类似于bash调用自己写的或者系统提供的函数。例如echo,cd(bash调用chdir系统接口实现)等