【Linux系统】—— 环境变量

【Linux系统】------ 环境变量

  • [1 概念介绍](#1 概念介绍)
  • [2 命令行参数](#2 命令行参数)
    • [2.1 main 函数有参数吗?](#2.1 main 函数有参数吗?)
    • [2.2 argc 与 argv](#2.2 argc 与 argv)
    • [2.3 命令如何通过不同参数实现不同子功能](#2.3 命令如何通过不同参数实现不同子功能)
  • [3 环境变量:PATH](#3 环境变量:PATH)
    • [3.1 查看环境变量](#3.1 查看环境变量)
    • [3.2 将路径添加进 PATH 中](#3.2 将路径添加进 PATH 中)
  • [4 环境变量从哪里来](#4 环境变量从哪里来)
    • [4.1 从存储的角度理解环境变量](#4.1 从存储的角度理解环境变量)
    • [4.2 环境变量最开始从哪来](#4.2 环境变量最开始从哪来)
  • [5 认识更多环境变量](#5 认识更多环境变量)
  • [6 对环境变量的操作](#6 对环境变量的操作)
    • [6.1 导入环境变量](#6.1 导入环境变量)
    • [6.2 删除环境变量](#6.2 删除环境变量)
    • [6.3 通过代码的方式获得环境变量](#6.3 通过代码的方式获得环境变量)
      • [6.3.1 main函数参数获取环境变量](#6.3.1 main函数参数获取环境变量)
      • [6.3.2 getenv 函数获取环境变量](#6.3.2 getenv 函数获取环境变量)
      • [6.3.3 全局变量 environ 获取环境变量](#6.3.3 全局变量 environ 获取环境变量)
  • [7 本地变量](#7 本地变量)

1 概念介绍

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

2 命令行参数

2.1 main 函数有参数吗?

问大家一个问题:main 函数有参数吗?

c 复制代码
int main()
{
	return 0;
}

好像我们平时写的 main函数 都是不用传参的,但不传参就代表不用参数吗?

main函数 至少有两个参数int argcchar *argv[]

c 复制代码
int main(int argc, char *argv[])
{
	return 0;
}

main函数 仅仅是我们自己的代码的入口,实际上 main函数 也要被其他函数(Linux中为 _start 函数)调用

2.2 argc 与 argv

现在我们知道 main函数 有两个参数 argcargv,他们是什么呢?有什么用呢?

argc 是 指针数组 argv 的长度

我们将 argv 的内容打印出来看看

c 复制代码
#include <stdio.h>

int main(int argc, char *argv[])
{
    int i = 0;
    for(; i < argc; ++i)
    {   
        printf("argv[%d]:%s\n", i, argv[i]);
    }   
    return 0;
}

运行:

我们发现argv:其实就是将命令以空格作为分隔符,一个一个放入指针数组中

我们在命令行中写的命令会被当成一个长串字符串,有人帮我们将命令以空格作为分隔符进行切分,被切分后的各个字符即命令行参数 。将命令行参数依次放入 argv数组 中,数组中有多少个参数由 argc 指明。同时有效元素放完必须以NULL结尾

2.3 命令如何通过不同参数实现不同子功能

看到这里,不知道大家有没有想到什么?

我们平时用的命令,他们本质上就是可执行程序,且一般是由 C语言 写的。我们可以在后面带命令行选项ls -a -l中的 -a-l。我们输入不同的选项,命令可以执行不同的子功能。如果做到的呢?不急,我们先写一段代码

c 复制代码
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    if(argc != 2)
    {   
        printf("Usage:%s [-a|-b|-c]\n", argv[0]);
        return 1;
    }   

    const char *arg = argv[1];
    if(strcmp(arg, "-a") == 0)
        printf("这是功能1\n");
    else if(strcmp(arg, "-b") == 0)
        printf("这是功能2\n");
    else if(strcmp(arg, "-c") == 0)
        printf("这是功能3\n");
    else
        printf("Usage:%s [-a|-b|-c]\n", argv[0]);

    return 0;
}

代码逻辑:如果未带选项或者选项错误,打印提示信息;否则执行对应子功能

所以:命令行参数是为了让一个程序可以通过选项实现不同的子功能。这也是指令选项的实现原理!

注:./code 也算一个命令行参数,所以判断语句是 if(argc != 2) 而不是 if(argc != 1)

谁帮我们进行切分的呢?是shell,Linux 中即 bash

为什么我们之前不用带命令行参数呢?因为不需要。我们之前写的代码功能就一个,如果我们想让我的代码有不同的子功能就可以带命令行参数

所以在进程启动时,有一张表 ------ argv表 ,用来支持实现选项功能。

3 环境变量:PATH

我们发现,执行我们指令的程序需要带 「./」,而执行系统的命令就不需要带 「./」。这是为什么呢?

无论是我们自己写的程序还是系统的指令,没有本质区别,都是二进制程序 。那为什么一个要带路径一个不用呢?

虽然不知道为什么,但有一点我们清楚:要执行一个程序,必须先找到他!

正因为要找到它,所以我们运行自己的程序要带「./」表明是在当前路径。我们执行系统命令就不用呢?原因是系统中存在环境变量 PATH,来帮助系统找到目标二进制文件。

环境变量 PATH 默认情况下在系统中存在,用来标识一串路径 ,以用来告诉系统去那些路径下找二进制文件

3.1 查看环境变量

怎么查看PATH环境变量呢?

在系统中,查看所有环境变量

使用指令:env

环境变量也是一个变量,环境变量的构成:名称=内容

只想看指定环境变量的内容:

使用指令:echo $环境变量名称

我们发现 PATH 中的内容都是以绝对路径 的形式呈现,以 : 作为分隔符

当我们输入 ls 等指令,系统不是直接去 /usr/bin 路径去查;而是依次遍历所有路径 ,最终在 /usr/bin 路径下找到 ls 指令。如果将所有路径遍历完依然没找到,就会报 Command not found。

3.2 将路径添加进 PATH 中

我们将自己当前的路径添加到 PATH 中,那我们写的 code 可执行也可以不带「./」直接就能被系统找到。

如何将路径添加进 PATH 中呢?

PATH 已经是一个变量,我们直接对其赋值

这样我们的指令就能不带 「./」 直接被系统找到了

但是,系统的指令找不到了

原因是刚刚我们 PATH=/home/gy/lesson13覆盖式的写,将 PATH 中原来的路径覆盖了

怎么改回来?不用担心,PATH是一个内存级 的变量,想恢复只需重新登录账号即可。

如何追加式的将路径写入PATH呢?如下图:

当然,如果再退出后登录,PATH 依然恢复默认值

4 环境变量从哪里来

4.1 从存储的角度理解环境变量

环境变量是由名称和内容组成 的,那环境变量是由谁来保存的呢?答案是 bash。

当我们一旦登录时,系统就会给我们创建一个 bash 进程。bash 从系统中读取所有的环境变量信息,然后在 bash 内部形成一张表 ------ 环境变量表。环境变量表和上面的 argv 一样都是指针数组。上述所查看的所有环境变量名称=内容都是一个个字符串,环境变量表指向这一个个字符串

我们输入的指令如:ls -a -b 首先是被 bash 拿到,bash将 ls -a -b拆分成 ls -a -b,构建一张命令行参数表。后拿着 ls 这个命令,在环境变量表中找到 PATH,再在 PATH 中的各个路径找 ls。所以指令的查找工作是由 bash 自己完成的

结论:bash 内部会有两张表:命令行参数表和环境变量表

4.2 环境变量最开始从哪来

我不登录不就没有 bash 了吗?那环境变量最开始从哪来呢?

环境变量是从系统相关的配置文件来。

bash 最开始启动时,会从配置文件中读取多有环境变量的信息,并在自己内部创建环境变量表

这个配置文件怎么看?

bash 复制代码
vim .bash_profile
vim .bashrc
vim /etc/bashrc

我们登录时,bash 就会被创建。bash 就会读取 .bashrc.bash_profile 配置文件的内容构建出环境变量表。

如果我们在配置文件中加入自己的路径,那么以后登录,我们的 code 就能不带路径自动被执行。

如果 Linux 中有 10 个用户登录,系统中就存在 10 个 bash。是不是意味着这 10 个 bash 每一个都要从配置文件中奖将环境变量读到自己的 bash 上下文中?所以每一个 bash 中都存在两张表:命令行参数表和环境变量表

5 认识更多环境变量

每一个环境变量都有其特定的功能,下面我们介绍几个

  • HOME:当前用户的家目录

  • SHELL:当前用户登录时,用的是哪一个版本的 shell。默认保存的是 bash 的路径(/bin/bash)

  • USER:当前用户

  • HISTSIZE:记录最大历史命令个数

  • PWD:当前的工作路径

  • OLDPWD:记录上一次所处的工作路径

所以为什么 cd - 可以做最近两次路径的切换?环境变量给我们提供好了

6 对环境变量的操作

前文,我们已经学会了两个获取环境变量的方法

  • env
  • echo $xxx

6.1 导入环境变量

导入环境变量:

使用指令:export 要导入变量名=要导入变量内容

bash 复制代码
export MYENV=11223344

不知大家有没有觉得奇怪

环境变量是在谁的上下文中的?是 bash。而 export 是命令,是一个由 bash 创建的子进程。进程之间是互不影响的。所以为什么在进程 export 中的操作会影响进程 bash 呢?会改变父进程的环境变量表呢?

只有一个解释:export 命令是一个内建命令

内建命令不需要创建子进程,由 bash 自己亲自执行。一般是由 bash 调用函数或者系统调用完成

6.2 删除环境变量

删除环境变量:

使用指令:unset 环境变量名

bash 复制代码
unset MYENV

6.3 通过代码的方式获得环境变量

6.3.1 main函数参数获取环境变量

main函数有参数吗?有!

main函数最多有几个参数?

3个!

那还有一个参数是谁呢?

最后一个是 char *env[]

c 复制代码
int main(int argc, char *argv[], char *env[])
{
	return 0;
}

char *env[]是什么呢?

char *env[] 是一个指针数组,未来父进程 (如:bash) 可以把环境变量表传给 main 函数,由 char *env[] 接收。环境变量表与命令行参数表的形式是一模一样的,最后都以NULL结尾

我们写一段代码验证一下

c 复制代码
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[], char *env[])
{
    (void)argc;
    (void)argv;

    int i = 0;
    for(; env[i]; ++i)
    {   
        printf("env[%d]->%s\n", i, env[i]);
    }   
	
	return 0;
}

注:(void)argc;的写法是为了不让编译器报警告。在 gcc 中如果我们定义了变量确不去用他会报警告,将其强转表示使用了 argc 变量,就不会报警告

此时 main函数 就获取了环境变量,这个环境变量是谁的呢?是父进程也就是 bash 的。

结论:环境变量可以被子进程继承 。环境变量在系统中具有全局特性

注:Linux 中 main 函数是由 _start 函数调用的,_start 会先扫描 main 函数的参数个数,再执行相应 main函数。大致如下:

c 复制代码
_start
{
	int ret = 0;
	int arg_count = 0;

	//扫描 main 函数的参数个数
	arg_count = 0/2/3;

	if(arg_count == 0)
		ret = main();
	else if(arg_count == 2)
		ret = main(argc, argv);
	else
		ret = main(argc, argv, env);
}

6.3.2 getenv 函数获取环境变量

输入指令:man 3 getenv 查看 getenv 函数


getenv:

  • 函数功能:获取一个环境变量的内容。获取成功返回内容的其实地址;否则返回NULL

代码实验:

c 复制代码
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[], char *env[])
{
    (void)argc;
    (void)argv;
    (void)env;

    char *value = getenv("PATH");
    if(value == NULL)
        return 1;
    printf("PATH->%s\n", value);
    
    return 0;
}

如果我只想让我写的程序只能我执行,就算 root 也没门,该怎么设计?

我们可以运用环境变量。

整个系统只有一个人知道登录用户是谁:bash。在 bash 中的环境变量表中记录着环境变量 USER,而 bash 可以将环境变量交给子进程,子进程可以根据环境变量进行身份识别

c 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[], char *env[])
{
    (void)argc;
    (void)argv;
    (void)env;

    char *who = getenv("USER");
    if(who == NULL)
        return 1;
    
    if(strcmp(who, "gy") == 0) 
        printf("这是程序正常执行逻辑\n");
    else  
        printf("只有 gy 能执行\n"); 
        
    return 0;
}

为什么全局变量要有全局特性呢?

因为这样子进程就可以将环境变量继承下去,子进程就可以结合环境变量做个性化操作。比如上面指定一个只能直接执行的程序。

6.3.3 全局变量 environ 获取环境变量

输入指令:man environ 全局变量 environ

environ 指向环境变量表

为什么 environ 是 char** 类型的,因为environ指向的是环境变量表 ,而环境变量表本身就是一个指针数组

注:使用 environ 之前,需将其声明

c 复制代码
#include <stdio.h>
#include <unistd.h>

extern char **environ;

int main(int agrc, char *agrv[])
{
    (void)agrc;
    (void)agrv;

    int i = 0;
    for(; environ[i]; ++i)
    {   
        printf("environ[%d]->%s\n", i, environ[i]);
    }   
    
    return 0;
}

上述三种获取环境变量的方法中,我们最常用的是第二种 ,因为法一和法三都是获取整张环境变量表,而法二获取的是单个环境变量的内容。

7 本地变量

先看操作:

如上图,我们可以直接在命令行中定义变量 ,我们将这个变量称为本地变量。

此时我们用 env 查看环境变量,发现并没有找到 i 变量。这也说明我们定义的 i 变量不是环境变量

查看本地变量:

使用指令:set

set 可以查看本地变量和环境变量

删除本地变量:

使用指令:unset 本地变量名

所以什么是本地变量呢?

本地变量不会被子进程继承,只会在 bash 内部被使用。


好啦,本期关于 环境变量 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 Linux 的学习路上一起进步!

相关推荐
我也想失去烦恼18 小时前
Linux系统/etc/hosts文件中配置了主机解析,但还是无法解析ip
linux·运维·服务器
德福危险20 小时前
密码枚举的艺术:靶机练习之midwest
服务器·安全·web安全
R-G-B20 小时前
【25】MFC入门到精通——MFC静态文本框 中字符串 连续输出 不覆盖先前的文本 换行输出
c++·mfc·mfc静态文本框输出字符串·mfc静态文本框连续输出字符串·mfc静态文本框换行输出字符串
deng-c-f20 小时前
Linux C/C++ 学习日记(29):IO密集型与CPU密集型、CPU的调度与线程切换
linux·学习·线程·cpu·io密集·cpu密集
ximy133521 小时前
AI服务器工作之整机部件(CPU+内存)
运维·服务器
weixin_4211334121 小时前
bisheng 的 MCP服务器添加 或 系统集成
运维·服务器
FFZero11 天前
【C++/Lua联合开发】 (二) Lua调用C++函数
c++·junit·lua
报错小能手1 天前
linux学习笔记(43)网络编程——HTTPS (补充)
linux·网络·学习
报错小能手1 天前
linux学习笔记(45)git详解
linux·笔记·学习
CoderCodingNo1 天前
【GESP】C++四级真题 luogu-B4068 [GESP202412 四级] Recamán
开发语言·c++·算法