目录
环境变量
一个现象的引入
main函数由参数吗------有的
bash
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("argc = %d\n", argc);
printf("命令行参数:\n");
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
hzx@VM-0-12-ubuntu:~$ gcc args.c -o args
hzx@VM-0-12-ubuntu:~$ ./args hello world 123
argc = 4
命令行参数:
argv[0] = ./args
argv[1] = hello
argv[2] = world
argv[3] = 123
| 参数 | 全称 | 含义 |
|---|---|---|
argc |
argument count | 命令行参数的个数 |
argv |
argument vector | 命令行参数的字符串数组 |
这个东西类比学习命令的时候
bash
ls -a -l /home/hzx
argc = 4
argv[0] = "ls"
argv[1] = "-a"
argv[2] = "-l"
argv[3] = "/home/hzx"
为什么这样设计?
| 原因 | 说明 |
|---|---|
| 统一 | 所有程序用同一套机制接收参数 |
| 灵活 | 参数个数不限,可多可少 |
| 简单 | 程序内部只需遍历 argv |
| 标准化 | 所有 Linux 命令都遵循这个规则 |
为什么系统的文件没有./,反而自己写的需要?
任何一个命令要执行他的时候要先找到他,./是指的是当前目录下,那是因为系统当中存在环境变量------PAPH,帮助找到文件。
可以通过把自己的文件放到和系统一样的路径下就可以了(要用root权限)------但不推荐
这里的PAPH环境变量,告诉系统当前文件的位置去执行。
在unbutun怎么查找环境变量的内容?------输入env,里面的结构是名称=内容
查找想要的echo $PHPY

怎么恢复?关掉unbutun重启就可以了。
那到底怎么才能把自己的文件放到系统的文件路径下,不会删掉系统文件的路径呢?
PATH=$PATH:文件路径

如何理解环境变量?
环境变量的值是被bash保存起来的,执行命令的时候是bash找到对应的路径的。bash形成一张表,叫做环境变量表,他就是指针数组。
看下图------下面一个一个的环境变量就是字符串,放到了一个指针数组里面。

这个这个指针数组可以说是形成了一张表,他是由bash形成的,例如执行ls -a -l的时候,他不是由子进程先拿到的,是先由bash拿到的,bash拿到程序的名字ls,再结合环境变量PATH,PATH里面有多个路径,把ls和PATH的多个路径进行拼接,如果命令在就创建子进程执行他。
结论:bash有两张表一个是环境变量表(argv),一个是命令行参数表(env)
环境变量最开始从哪里来的?
从配置文件来的,系统启动,bash会读取配置文件所有包括环境变量直接或间接的值,并创建环境变量表。
总结:argv和env的配合
命令行参数表(argv)和环境变量表(env)是程序启动时由 main 函数同时接收的两组信息,二者分工明确、配合紧密:argv 来自用户在命令行输入的参数,告诉程序"要做什么" ;env 来自 shell 继承的系统环境变量,告诉程序"在哪里找资源、用什么配置" 。程序运行时,先从 argv 解析出用户意图,再从 env 读取运行环境,两者结合才能正确完成任务------缺了 argv,程序不知道用户想干什么;缺了 env,程序找不到依赖的库和配置,也无法正常运行。
以 ls -l /home/hzx 为例,当你在终端敲下这条命令时,shell 会先解析它,把命令名和参数拆成 argv = ["ls", "-l", "/home/hzx"],同时把当前 shell 的环境变量(如 PATH、HOME、LANG 等)一起通过 exec 系列函数传递给新创建的 ls 进程;ls 的 main 函数收到 argc = 3 和上述 argv、env 后,会遍历 argv 识别出 -l 这个选项并设置"详细列表"标志,同时把 /home/hzx 记录为要列出的目标目录,接着它利用 env 里的 PATH 找到自己需要的动态库、利用 LANG 决定显示语言,然后调用 opendir 和 readdir 打开并读取 /home/hzx 目录内容,对每个文件调用 stat 获取权限、大小、时间等信息,最后按照 -l 要求的格式把结果输出到终端------整个过程就是 argv 告诉 ls"做什么"(详细列出 /home/hzx),env 告诉 ls"在哪找资源、用什么配置"(库路径、语言环境),二者配合完成这条命令。
其他环境变量
USER和LOGNAME
USER:当前用户
LOGNAME:登录的用户
su -:输入他的时候他会切换成root账号,对应的USER和LOGNAME都会变
bash
su - 相当于:
1. 退出当前 shell
2. 以 root 身份重新登录
3. 重新加载所有环境变量
4. USER 和 LOGNAME 都变成 root
sudo:切换的时候USER会变,LOGNAME不变
bash
sudo 相当于:
1. 保持当前 shell 环境不变
2. 临时以 root 身份执行命令
3. USER 变成 root(有效身份变了)
4. LOGNAME 不变(原始登录用户没变)
HISTSIZE=1000
当用上下箭头的时候,可以查看历史命令,但他只能查找最近的1000条。
PWD
记录shell的当前位置
OLDPWN
上一次shell所在的路径,每次cd到一个新的路径都会更新他
活动环境变量的方法
创建环境变量
exprot 名称=内容
将自己创建的环境变量放到环境变量表里面
取消环境变量
unset 名称
以代码的方式获得环境变量
方法1:main函数参数打印
实际上main函数有三个参数
bash
int main(int argc, char *argv[], char *env[])
env[0] = "SHELL=/bin/bash"
env[1] = "PWD=/home/hzx"
env[2] = "HOME=/home/hzx"
env[3] = "USER=hzx"
env[4] = "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
...
env[n] = NULL
直接打印可以获得环境变量的信息
| 参数 | 全称 | 含义 |
|---|---|---|
argc |
argument count | 命令行参数个数 |
argv |
argument vector | 命令行参数数组 |
env |
environment | 环境变量数组 |
问题:这个环境变量是谁的?父进程(bash)
环境变量可以被子进程继承,也可以被孙子进程继承......------因此环境变量有全局特性
为什么要这么设计?
方法2:getenv系统调用
getenv这个也是个函数,他是根据环境变量的名字获取环境内容
bash
#include <stdio.h>
#include <stdlib.h>
int main() {
char *path = getenv("PATH");
char *home = getenv("HOME");
char *user = getenv("USER");
printf("PATH = %s\n", path);
printf("HOME = %s\n", home);
printf("USER = %s\n", user);
return 0;
}
一个只有特定用户执行的程序
bash
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// 1. 从环境变量获取当前用户名
char *user = getenv("USER");
// 2. 检查是否获取成功
if (user == NULL) {
printf("错误:无法获取 USER 环境变量\n");
return 1;
}
// 3. 判断是否是允许的用户
if (strcmp(user, "hzx") == 0 || strcmp(user, "root") == 0) {
printf("欢迎,%s!你有权限执行此程序。\n", user);
printf("程序正在执行...\n");
// 这里放真正要执行的逻辑
} else {
printf("抱歉,%s,你没有权限执行此程序。\n", user);
printf("只有 hzx 和 root 可以执行。\n");
return 1;
}
return 0;
}
方法3:environ
environ是一个全局变量
| 项目 | 说明 |
|---|---|
| 类型 | char **(字符指针数组) |
| 声明 | extern char **environ |
| 头文件 | 无(需要手动 extern 声明) |
| 内容 | 所有环境变量 |
| 结尾 | environ[n] = NULL |
cpp
#include <stdio.h>
// 声明全局变量 environ
extern char **environ;
/*这个变量在别处定义(libc 里)
这里只是声明,不分配内存
链接时找到真正的定义
*/
int main() {
// 遍历环境变量表
for (int i = 0; environ[i]; i++) {
printf("environ[%d] = %s\n", i, environ[i]);
}
return 0;
}
如果非要选一个建议用方法3
环境变量的特性
| 特性 | 说明 |
|---|---|
| 全局性 | 所有进程都能访问 |
| 继承性 | 父进程传给子进程 |
| 持久性 | 登录期间一直存在 |
| 可修改性 | 可以动态设置、删除 |
| 键值对形式 | KEY=VALUE 格式 |
| 字符串类型 | 值只能是字符串 |
| 区分大小写 | PATH 和 path 不同 |
小概念:本地变量
他和环境变量都在bash内部使用的,但是本地变量不会传给子进程,只会在bash内部使用。
这个之前的pwd和export解释有联系,他和ls那些命令不同,是内建命令,不会依赖环境变量,因此他是由bash亲自去完成的,bash自己调用系统函数