【Linux】进程概念(四)(命令行参数和环境变量)
目录
- 前言
- 一、命令行参数
- 二、环境变量
- 1. 基本概念
- 2. 常见环境变量
- echo $ NAME :查看某个环境变量
- env 显示所有环境变量
- dev/pts/0 字符设备
- OLDPWD 保存上一次所处路径
- 3. 多方法获取环境变量
- 使用main函数
- 通过函数获取单个环境变量
- 全局指针获取
- 子进程如何获得环境变量
- 实践
- 4. 环境变量的全局性
- 三、内建命令引入
前言
书接上文【Linux】Linux进程概念(三)(进程状态,僵尸进程,孤儿进程,进程优先级)详情请点击查看。今天继续讲解【Linux】进程概念(四):(命令行参数和环境变量)
一、命令行参数
命令行参数是程序运行时从命令行传递给程序的参数,命令行参数允许用户在启动程序时提供额外的信息或配置选项
问题引入:main函数是否有参数?可以有几个参数?具体是哪几个参数?
- 现在我们直接编写一段C语言代码来回答上面的问题,main函数是有参数的,我们一定见过下面代码main函数的参数,但是argv指向的字符串到底是什么呢?
- 我们来编写一个代码来打印出数组指向的字符串
- 从上面的结果可以知道,argv[0] 是程序名,argv[1-n]是程序运行时命令行传递给程序的程序名后面的选项,有了这些我们就可以通过不同的选项来达到不同的功能
#include <stdio.h> #include <string.h> int main(int argc, char* argv[]) { if(argc != 2) { printf("传入参数错误,使用-v1/-v2/-v3\n"); return 1; } if(strcmp(argv[1], "-v1") == 0)printf("功能1\n");else if(strcmp(argv[1], "-v2") == 0)printf("功能2\n"); else if(strcmp(argv[1], "-v3") == 0) printf("功能3\n"); else printf("功能不支持\n"); return 0; }

- 我们使用指令
ls、ls -l、ls -a -l等,指令的本质就是C语言程序,通过在命令行输入不同的选项(选项本质就是字符串)可以以一定方式传递给ls内部的main函数,在ls内部实现的时候,就可以根据不同的选项实现类似功能的不同表现形式
- 在命令行输入相关指令或者运行自己的代码之后(
ls -l、./testArg -v1),是谁拿到了这个字符串信息:答案是父进程bash拿到了,父子进程共享数据和代码,所以子进程也能拿到参数信息,但是如何传给argv数组,后面程序替换时再具体讲解
在argv数组中存储字符串,最后一位保存的是NULL
因此打印argv数组中字符串,不仅可以使用上上面的方法,还可以使用下面代码来打印#include <stdio.h> #include <string.h> int main(int argc, char* argv[]) {int i = 0;for(; argv[i]; i++){printf("argv[%d] : %s\n", i, argv[i]); } return 0; }
总结:
- 命令行参数至少有一个(程序名)
- argv[0] 对应是程序名
- 有几个子串(串与串之间空格隔开),argc就等于几
二、环境变量
1. 基本概念
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,环境变量通常具有某些特殊作用,同时它还具有
全局属性
- 我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找
2. 常见环境变量
- PATH:指定命令的搜索路径(操作系统查找可执行命令是在环境变量PATH中查找的
- 对于指令
ls、pwd等直接在命令行输入就可以执行,但是自己的可执行程序直接a.out会找不到,原因就是指令直接保存在系统路径/usr/bin路径下,输入指令后会去系统路径去找,找到了就执行,没有找到就会报错:没有找到该执行文件- 所以如果是自己的程序执行,可以使用./a.out来执行,如果不想带路径,那么就可以把当前的可执行程序路径加入到环境变量中
- 加入指令为:
expert PATH=$PATH:路径
- HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL:当前Shell,它的值通常是/bin/bash
echo $ NAME :查看某个环境变量
- PATH环境变量以冒号(:)为分割符

env 显示所有环境变量

dev/pts/0 字符设备
- 当前登录的设备名,echo “aaaa” 默认就是打印在当前设备名的显示屏上,如果想要指定设备,echo “aaaa” > /dev/pts/对应的序号

OLDPWD 保存上一次所处路径
- OLDPWD是保存上一次所处路径,因此
cd -才能返回上一路径,cd -实质就是cd $OLDPWD

在进入操作系统时,系统会默认自动初始化, 执行pwd、进入Linux操作系统默认进入的家目录等,之所以能知道当前在哪个路径、家目录在哪,都是因为有环境变量的存在
3. 多方法获取环境变量
使用main函数
在命令行参数我们知道了,main函数有argc和argv两个参数,现在我们在来引入另外一个参数:env
#include <stdio.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;
}
结果如下:
char* env[]和char* argv[]结构是一样的,都是保存的字符串数据,且最后一个都是保存的NULL
通过函数获取单个环境变量

- 使用getenv函数来获取某个环境变量
#include <stdio.h>#include <string.h>#include <stdlib.h>int main(int argc, char* argv[], char* env[]){char* path = getenv("PATH");printf("PATH:%s\n", path); return 0;}

全局指针获取
- C语言提供了一个全局指针
- environ是一个二级指针,指向的是环境变量的环境变量表

#include <stdio.h>#include <string.h>#include <stdlib.h>int main(int argc, char* argv[], char* env[])
{extern char ** environ;int i = 0;for(; environ[i]; i++){printf("environ[%d] : %s\n", i, environ[i]); }return 0:}
子进程如何获得环境变量
- 获得环境变量的是父进程bash,并形成的环境变量表,环境变量表是父进程数据,子进程继承了父进程的代码和数据
总结:
- bash有两张表:命令行参数表(一直在变)、环境变量表(比较稳定),因为命令行参数一直在变,这两张表是内存级的
- 命令行参数bash能从命令行获取,但是bash的环境变量又是从哪里来呢?从系统的配置文件中来,
实践
- 实现一个程序只有当前用户(本人)才能执行代码功能
#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){char* who = getenv("USER");if(strcmp(who, "gy")){printf("不是指定用户,不能执行, %s\n", who); return 1;}printf("这是一个只有我自己能执行的程序\n");return 0;}


2.实现pwd
#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){char* pwd= getenv("PWD");printf("%s\n", pwd);return 0;}

- 从前面的学过的知识我们可以知道,进程会记录下自己的工作路径cwd,bash也有自己的cwd,且保存在task_strcut中,因此子进程会以父进程为模板创建,所以子进程的pwd从父进程bash来。但是父进程的cwd又怎么获得的呢?
- 使用函数getcwd,系统调用获得当前pwd来更新环境变量

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>int main(){char pwdbuf[128];char *pwd = getcwd(pwdbuf, sizeof(pwdbuf));printf("%s\n", pwd);return 0;}

注意:
环境变量大部分从配置文件中来,也有少部分是启动之后动态获取或者创建的
4. 环境变量的全局性
我们所运行的进程是子进程,bash本身在启动的时候会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程交给子进程的环境变量,bash是所有进程的父进程,bash创建的子进程有可能再创建子进程,那么此时的进程关系就形成了一颗多叉树的形状,那么对应由于子进程要继承父进程的环境变量,而bash又是所有进程的父进程,那么bash中的环境变量在全部进程中都会有一份,那么此时环境变量就具有全局属性
本地变量,即在bash命令行中定义出的变量是本地变量,本地变量只在本bash内部有效,不会被子进程继承,本地变量不同于环境变量,本地变量不归属环境变量,同样环境变量不归属本地变量
- export 变量名(= ?):设置一个新的环境变量
- unset 变量名:清除环境变量
- 本地变量可以通过export将自己变为环境变量
- 当本地变量使用export变成环境变量之后,可以通过unset将环境变量再变回本地变量
- 可以使用set查看环境变量和本地变量
三、内建命令引入

- 观察上图小编在bash命令行中定义了一个本地变量myset,本地变量myset不会被子进程继承,那么根据小编之前的理论,指令是一个二进制可执行程序,那么echo的本质就是创建了一个子进程,但是为什么echo这个子进程可以查看本地变量myset的值呢?
- 究其原因是大部分命令是可执行程序,需要通过创建子进程的性质来执行。但是在Linux中,还有一部分命令执行的时候本身没有风险,需要bash自己执行,这样的命令叫做内建命令







