当前位置: 首页 > news >正文

环境变量

目录

一.概念介绍

1.1命令行参数

二.一个例子,一个环境变量

2.1查看环境变量 

2.2如何理解环境变量呢?存储的角度

2.3环境变量最开始从哪里来的呢?

概括:

1. 环境变量的存储

2. 命令查找过程

3. 环境变量表和命令行参数表

4. 示例

总结

三.认识更多的环境变量

常用环境变量及其作用

四.获取环境变量的方法

1. 使用命令行工具

1.1 env 命令

1.2 printenv 命令

1.3 echo 命令

1.4 set 命令

2. 在脚本中获取环境变量

2.1 使用 $ 符号

2.2 使用 ${} 语法

3. 在编程语言中获取环境变量

3.1 C 语言

​编辑

3.2 Bash 脚本

4. 获取环境变量的值并赋值给变量,以及增删变量

4.1 使用 read 命令

4.2 使用 export 命令

4.3 使用 unset 命令 

5. 获取环境变量的值并进行操作

5.1 使用 cut 命令

5.2 使用 awk 命令

五.main函数的问题 

main函数最多有几个参数?

六.理解环境变量的特性

6.1环境变量具有全局特性

总结

6.2补充两个概念:为后面埋伏笔

a.Shell 变量赋值规则

b.导入环境变量(export XXX)是导到谁里面了? 


一.概念介绍

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

1.1命令行参数

main函数有参数吗?

在C语言中,main函数可以有参数,也可以没有参数。它的常见形式有两种:一种是没有参数的int main(),另一种是带参数的int main(int argc, char* argv[])。其中,argc表示命令行参数的个数,argv是一个字符串数组,存储了命令行参数的内容(char* 要么指向字符的地址,要么指向字符串的地址)

在Linux操作系统中,main函数是由_start函数调用的。_start函数是程序的入口点,它是由操作系统加载程序时启动的。_start函数会做一些初始化工作,比如设置栈、初始化全局变量等,然后调用main函数。当main函数执行完毕后,会返回一个值给_start函数,_start函数再根据这个返回值调用exit函数来结束程序。

//code.c
#include <stdio.h>//main函数有参数吗
int main(int argc, char* argv[])
{for(int i = 0;i < argc;i++){printf("argv[%d]:%s\n", i, argv[i]);}return 0;
}

编译运行结果:

lfz@hcss-ecs-ff0f:~/lesson/lesson15$ ./code
argv[0]:./code
lfz@hcss-ecs-ff0f:~/lesson/lesson15$ ./code a
argv[0]:./code
argv[1]:a
lfz@hcss-ecs-ff0f:~/lesson/lesson15$ ./code a b
argv[0]:./code
argv[1]:a
argv[2]:b
lfz@hcss-ecs-ff0f:~/lesson/lesson15$ ./code a b c
argv[0]:./code
argv[1]:a
argv[2]:b
argv[3]:c

 argc表示命令行参数的数量,argv是一个字符串数组,存储了命令行输入的参数值。从运行结果可以看出,argv[0]总是程序的名称(这里是./code),以空格为分隔符,后续的argv[1]argv[2]等依次对应输入的参数。例如,运行./code a b c时,argc为4,argv数组依次存储了"./code""a""b""c"

我们知道,我们平时用的命令,他们本质上就是可执行程序,而我们用到的这些命令,他一般也是用C语言写的,而他用C语言写的,我们可以在命令后添加一个或多个选项:

我们先来看一个代码例子:

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;
}

从中我们可以看到main函数的命令行参数的用途是可以让一个程序可以通过选项,可以实现不同的子功能。则也是我们之前使用指令的时候为什么会有选项,因为main函数可以带命令行参数的!!!(指令选项的实现原理)

命令行中输入的字符串最终会被Shell(如Bash)进行切分处理,然后将切分后的字符串作为参数传递给程序,构建argv[]数组。

Shell的字符串切分机制(切分方法之一)

  • 当你在命令行中输入一个命令时,Shell会根据默认的分隔符(如空格、制表符等)将输入的字符串切分成多个部分。这些部分被存储在argv[]数组中,其中argv[0]通常是程序的名称,argv[1]argv[2]等则是用户输入的参数。

  • 例如,输入命令./example -a param1时,Shell会将其切分为./example-aparam1,分别存储到argv[0]argv[1]argv[2]中。

所以在我们的进程启动时,我们的进程拥有一张表,叫做argv表,用来支持实现选项功能。

二.一个例子,一个环境变量

我们发现我们执行自己的命令的时候(比如:./code)是要带" ./ "的,而在执行系统命令的时候是不带" ./ "的:

无论是否在系统当中,我们写的二进制程序和系统的指令是没有本质区别的,我们使用的指令本来就是系统预装的二进制程序,那为什么一个带路径,一个不用带呢?

虽然现在我们还解释不清楚,但是我们有一点应该知道:

要执行一个程序,必须先找到它!!!

所以我们运行我们自己的程序时,我们需要" ./ ",就是表明我们要执行的程序在当前路径下,可是为什么执行我们的系统命令就不需要呢?原因是系统当中存在环境变量,来帮助找到目标二进制文件!!!

lfz@hcss-ecs-ff0f:~/lesson/lesson15$ ls /usr/bin/ls
/usr/bin/ls

这是ls命令所在的路径。

要执行命令,首先需要找到他,所以我们运行我们的程序时,我们直接 code -a 他就找不到,所以执行一个命令的时候,默认不会在我们当前路径下找的,他不会找的!

换句话说:如果我们把code拷贝到/usr/bin目录下,是不是我们在执行code的时候就不需要再带路径了呢?答案是:是的。我们看看下面的试验:

我们(非root)直接拷贝到指定目录下是不行的:

他的拥有者/所属组是root,对于other不给写的权限:

我们应该使用:

sudo cp code /usr/bin/

我们就可以得到: 

我们就可以直接不带路径就可以使我们的二进制程序运行了。

但是我们强烈不建议将我们写的二进制文件拷贝到系统当中,因为我们写的二进制文件没有经过严格的测试,他人的发布流程,代码也没有经过时间的验证,代码可能会有bug,可能污染原本系统的指令池:我们测试完就将其删除吧☺ 

sudo rm /usr/bin/code

我们完整的过程验证了,但是这里就出现了新的问题了,我们的最终结论就是:系统是默认认识/usr/bin/路径目录下的,所以我们将我们写的二进制程序cp到该目录下,系统就认识了,就不需要指定路径了,可是系统凭什么它能够认识/usr/bin/路径下,他怎么就知道我们执行命令的时候就必须得去我们对应的/usr/bin/路径下去查呢?

因为系统当中会存在环境变量,这个环境变量就是:PATH

所以在Linux系统当中,存在一个环境变量PATH,这个环境变量PATH,他默认情况下,是在我们系统当中存在的,用来标识一串路径,PATH里面表明的是:告诉系统去哪些路径下找二进制文件,就是在系统中搜索指令的默认搜索路径!!! 

2.1查看环境变量 

在Linux系统当中,我们想要查看所有的环境变量,我们可以使用:

env
#environment的简写

将系统当中所有的环境变量给罗列出来。

环境变量他是一个变量,所以环境变量的构成是:

名称+内容

我们标识环境变量的唯一性,我们都是通过名称来标识的。

我们想要看一个环境变量的内容,我们可以使用:

echo $环境变量名称
#要加$,不让就变成了直接打印字符串了

我们系统在搜索某些命令时,就默认在这些路径下搜索,并且这些路径都是以绝对路径的方式呈现的,我们发现路径与路径之间会有" : ",说人话就是,我们要执行一个" ls "命令的时候,操作系统他不是默认在/usr/bin/路径下,默认是去查这个PATH环境变量,而PATH环境变量找的时候,他会以" : "作为分隔符,现在第一个路径找,没有找到" ls "就接着往后面找,以此类推,如果把所有路径都遍历一次,发现都没有找到,就会给我们报:command not found 

找到了" ls ",我们就加载" ls "并执行他。

正是因为/usr/bin/在环境变量里,所以" ls "命令,才能够正常的被系统层(Shell)找到,以不带路径执行" ls "

那么归根结底在系统内找到我们对应的某一个命令,某一个二进制文件,默认是在环境变量PATH所对应的这些子路径当中一个一个的去找,所以我们上面将我们的"code"拷贝到/usr/bin路径下,这个"code"就能够直接被系统直接找到了,那要是把我们自己当前的路径添加到PATH这个环境变量里,那么此时我们自己不就可以不使用上面的方式到达直接使用"code"命令,不用直接显示路径来执行"code"命令了,这是对的,那么我们应该如何添加呢?

因为PATH本来就是一个环境变量,我们直接可以使用:

PATH=我们要填入的绝对路径(填当前,就是当前pwd的结果)

但是我们发现PATH的内容直接被覆盖了,这也是我们对变量赋值的行为:(ls等指令直接使用不了了)

所以我们不应该直接赋值进而导致覆盖,我们可以将其":"后接上我们要填入的绝对路径!!!

当然,我们重新启动我们的Shell,就可以回到最初状态,恢复原本的环境变量内容。

2.2如何理解环境变量呢?存储的角度

我们环境变量的值最终都是被谁保存起来的呢?包括我们执行命令时,是谁来在系统里找我这个命令呢?

答案是:bash!,也就是我们一旦登入的时候,系统就会给我们创建一个bash进程,而bash就必须从系统当中读取我们所有的环境变量的信息,然后在bash进程内部形成一张表,称为环境变量表,就是一个指针数组:

表的结尾为NULL,可用于遍历。 

我们所查看到的所有环境变量,其实是一个一个的字符串,0指针指向第一个,1指针指向第二个,依次类推,所以在bash进程启动的时候,在他自己内部会构建一张表,当我们在输指令时,假设输入" ls -a -b "时,我们已经知道了,首先这个命令行,这个字符串并不是被子进程拿到的,是先被bash先拿到,这时候就会构建第一张表:命令行参数表,接着bash拿着命令的名字,找到第二张表:环境变量表,再结合环境变量PATH,再根据PATH一个个的路径,把每个路径拼上我们的程序名,然后由bash在系统当中找对应的命令是否存在,如果在,就创建子进程再运行他。

总结就是:bash内部有两张表,一个叫作命令行参数表,一个叫作环境变量表。

所以环境变量就是K_Value的长字符串,那么在bash启动的时候,他会想办法在我们自己的bash内部(bash是C/C++写的程序),他new/malloc出一段空间,然后个每个环境变量再new/malloc出一段空间,形成一个二维数组,然后把环境变量字符串依次拷贝到这个表里面,所以bash就在内部维护了这张表,所以我们env查的时候,就是打印这张表的内容。

2.3环境变量最开始从哪里来的呢?

是由系统的相关配置文件中来的!!! 

所谓的环境变量的配置信息是在系统上,但是系统在每一个用户的家目录中存了两个隐藏文件:(CentOS)

用户可以在自己的主目录中定义个人的环境变量,如~/.bashrc~/.bash_profile等。这些文件在用户登录时加载。

我使用的是Ubuntu,所以在.profile文件当中:

我们就可以在此当中添加我们的路径信息:

在Ubuntu中,.profile文件是用户登录时由Shell(如bash)读取的配置文件之一。你可以在.profile文件中添加自定义的路径信息,以便在登录时自动设置环境变量(如PATH)。

以下是如何在.profile文件中添加路径信息的步骤:

1. 打开.profile文件

在终端中输入以下命令,打开.profile文件:

vim ~/.profile

2. 添加路径信息

vim中,你可以通过以下步骤添加路径信息:

步骤1:进入插入模式

  • 按下i键进入插入模式,光标会变成插入状态(通常会显示-- INSERT --)。

步骤2:定位到合适的位置

  • 使用方向键将光标移动到你想要添加路径信息的位置。通常建议将路径信息添加到文件的末尾,或者在PATH相关的部分。

步骤3:添加路径信息

  • 在插入模式下,输入以下内容(假设你要添加~/bin~/.local/binPATH):

    # Add custom directories to PATH
    export PATH="$HOME/bin:$HOME/.local/bin:$PATH"

3. 保存并退出

完成编辑后,按照以下步骤保存并退出vim

步骤1:退出插入模式

  • 按下Esc键退出插入模式。

步骤2:保存并退出

  • 输入以下命令并按下Enter键:

    :wq
    • :w表示保存文件。

    • :q表示退出vim

    • :wq表示保存并退出。

4. 使更改生效

为了让更改立即生效,可以运行以下命令:

source ~/.profile

或者,你也可以重新登录系统,使更改生效。

5. 验证更改

运行以下命令,检查PATH变量是否包含你添加的路径:

echo $PATH

你应该会看到类似以下输出(包含你添加的路径):

/home/your_username/bin:/home/your_username/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

6. 示例

假设你有一个自定义的脚本目录~/scripts,你可以将其添加到PATH中:

# Add ~/scripts to PATH
export PATH="$HOME/scripts:$PATH"

保存并使更改生效后,你就可以直接在命令行中运行scripts目录中的脚本了。

7. 注意事项

  • 避免重复添加路径:在添加路径之前,确保该路径尚未存在于PATH中,以避免重复。

  • 使用export命令:确保使用export命令,使变量在子进程中可用。

  • 备份.profile文件:在修改.profile文件之前,建议备份原始文件:

    cp ~/.profile ~/.profile.bak

通过在.profile文件中添加路径信息,你可以方便地管理和使用自定义的命令和脚本。

操作我们对于本篇不算很重要,重要的是我们要知道环境变量是从我们系统的配置文件中来的。 

概括:

1. 环境变量的存储
  • 操作系统配置文件

    • Linux/Unix:环境变量通常在系统级配置文件中定义,如/etc/environment/etc/profile/etc/bashrc等。这些文件在系统启动时被加载,设置全局环境变量。

    • Windows:环境变量存储在注册表中,系统启动时加载这些变量。

  • 用户配置文件

    • Linux/Unix:用户可以在自己的主目录中定义个人的环境变量,如~/.bashrc~/.bash_profile等。这些文件在用户登录时加载。

    • Windows:用户可以通过“系统属性”中的“环境变量”界面设置用户级的环境变量。

  • Shell启动时加载

    • 当用户登录时,系统会启动一个bash进程。bash会读取系统和用户配置文件中的环境变量,并在内部构建一个环境变量表。

    • 环境变量表是一个指针数组,每个指针指向一个环境变量的字符串。这些字符串以\0结尾,形成一个二维数组。

2. 命令查找过程
  • 命令行输入

    • 当用户在命令行中输入一个命令(如ls -a -b)时,bash会首先获取这个命令行字符串,并将其拆分成命令行参数表。

    • 命令行参数表是一个指针数组,每个指针指向一个参数字符串。

  • 环境变量表

    • bash会结合环境变量表中的PATH变量,查找命令的可执行文件。

    • PATH变量定义了命令的搜索路径,路径之间用冒号(:)分隔。bash会依次在这些路径中查找命令。

  • 查找和执行

    • bash会将命令名与PATH中的每个路径拼接,形成完整的路径名,并在系统中查找对应的可执行文件。

    • 如果找到可执行文件,bash会创建一个子进程来运行该命令。

3. 环境变量表和命令行参数表
  • 环境变量表

    • 环境变量表是一个指针数组,每个指针指向一个环境变量的字符串。这些字符串以\0结尾,形成一个二维数组。

    • bash在启动时会读取系统和用户配置文件中的环境变量,并将这些变量存储在环境变量表中。

  • 命令行参数表

    • 命令行参数表是一个指针数组,每个指针指向一个命令行参数的字符串。

    • 当用户输入命令时,bash会将命令行字符串拆分成命令行参数表,并在内部构建这个表。

4. 示例
  • 环境变量表

    • 假设PATH变量的值为/usr/local/bin:/usr/bin:/binbash会将这些路径存储在环境变量表中。

    • 当用户输入ls时,bash会依次在这些路径中查找ls的可执行文件。

  • 命令行参数表

    • 当用户输入ls -a -b时,bash会将命令行字符串拆分成命令行参数表,如下:

      • argv[0] = "ls"

      • argv[1] = "-a"

      • argv[2] = "-b"

总结
  • 环境变量的存储环境变量最初由操作系统和用户配置文件定义,bash在启动时读取这些变量,并在内部构建环境变量表。

  • 命令查找过程bash通过环境变量表中的PATH变量查找命令的可执行文件,并创建子进程来运行命令。

  • 内部表结构bash内部维护了两张表,一张是环境变量表,另一张是命令行参数表,用于管理和执行命令。

最后,如果Linux系统有10个用户登入呢?

我们运行的命令的父进程都是bash,那么有10个用户登入,就要有10个bash,就需要从对应的配置文件当中读到自己的bash上下文里。 

指令的查找工作是由bash自己完成的,上面的“执行一个命令就需要找到他”,就是bash在找,通过PATH来找,也就是通过环境变量来找!!!

在Windows中,你可以通过系统属性中的“环境变量”选项配置环境变量,包括用户变量和系统变量。具体步骤是:右键点击“此电脑”选择“属性”,进入“高级系统设置”,点击“环境变量”,在弹出的窗口中可以新建或编辑用户变量和系统变量。 

三.认识更多的环境变量

  • 常用环境变量及其作用

    环境变量名称作用
    PATH指定可执行文件的搜索路径
    HOME存储用户的主目录路径
    SHELL指示用户使用的 shell 类型
    LANG 和 LC_*设置语言环境和区域设置
    TERM指示终端类型
    EDITOR 和 VISUAL指定默认的文本编辑器
    PS1定义命令行提示符
    MAIL指示邮件存放的位置
    LOGNAME显示当前登录用户的用户名
    HISTFILE指定命令历史记录文件的位置
    TZ设置时区
    LD_LIBRARY_PATH 或 DYLD_LIBRARY_PATH指定动态链接库的搜索路径

四.获取环境变量的方法

在 Linux 系统中,可以通过多种方法获取环境变量。以下是详细的获取方法:

1. 使用命令行工具

1.1 env 命令
  • 功能:显示当前所有的环境变量及其值。

  • 语法

    env
  • 示例

    env | less

    这将显示所有环境变量,并通过 less 命令分页查看。

1.2 printenv 命令
  • 功能:显示当前所有的环境变量及其值,类似于 env 命令。

  • 语法

    printenv
  • 示例

    printenv | grep PATH

    这将显示包含 PATH 的环境变量。

1.3 echo 命令
  • 功能:显示指定环境变量的值。

  • 语法

    echo $变量名
  • 示例

    echo $PATH

    这将显示 PATH 环境变量的值。

1.4 set 命令
  • 功能:显示当前 shell 中的所有变量,包括环境变量和局部变量。

  • 语法

    set
  • 示例

    set | grep PATH

    这将显示包含 PATH 的变量。

2. 在脚本中获取环境变量

2.1 使用 $ 符号
  • 功能:在脚本中获取环境变量的值。

  • 语法

    $变量名
  • 示例

    #!/bin/bash
    echo "PATH: $PATH"
    echo "HOME: $HOME"
2.2 使用 ${} 语法
  • 功能:在脚本中获取环境变量的值,支持变量名为空或包含特殊字符的情况。

  • 语法

    ${变量名}
  • 示例

    #!/bin/bash
    echo "PATH: ${PATH}"
    echo "HOME: ${HOME}"

3. 在编程语言中获取环境变量

3.1 C 语言
  • 功能使用 getenv 函数获取环境变量的值。(通过系统调用)putenv , 后⾯讲解

  • 语法

    #include <stdlib.h>
    char *value = getenv("变量名");
  • 示例

    #include <stdio.h>
    #include <stdlib.h>int main() {char *path = getenv("PATH");if(psth == NULL)return 1;printf("PATH: %s\n", path);return 0;
    }

如果我想写一个程序,只有我这个用户能执行,其他人,包括root一律不让执行,我们应该如何设计?

对于环境变量的认识,我们知道,现在只有一个人知道登入用户是谁,这个人就是bash,也就是说在整个系统当中,只有bash知道登入用户是谁,我们可以如下设计:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char* argv[], char* envp[])
{(void)argc;(void)argv;(void)envp;const char* who = getenv("USER");if(who==NULL){return 1;}if(strcmp(who,"lfz")==0){printf("这是程序的正常执行逻辑!\n");}else{printf("Only lfz!!!\n");}return 0;
}

  • 功能使用environ,这是一个二级指针,因为我们环境变量表就是一个二维数组
  • 语法:要声明:

  • 示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>//声明
extern char**environ;int main(int argc, char* argv[])
{(void)argc;(void)argv;for(int i=0;environ[i];i++){printf("environ[%d]->%s\n", i, environ[i]);}return 0;
}
3.2 Bash 脚本
  • 功能:在 Bash 脚本中获取环境变量的值。

  • 语法

    $变量名
  • 示例

    #!/bin/bash
    echo "PATH: $PATH"
    echo "HOME: $HOME"

4. 获取环境变量的值并赋值给变量,以及增删变量

4.1 使用 read 命令
  • 功能:将环境变量的值赋值给变量。

  • 语法

    read 变量名 <<< $(env | grep 变量名 | cut -d'=' -f2)
  • 示例

    read path <<< $(env | grep PATH | cut -d'=' -f2)
    echo "PATH: $path"
4.2 使用 export 命令
  • 功能:将变量赋值给环境变量。也可以在环境变量中新增一组K_Value(环境变量)(有则改,无则增)

  • 语法

    export 变量名=值
  • 示例

    export PATH=$PATH:/new/pathexport MYENV=11223344
4.3 使用 unset 命令 
  • 功能:删除环境变量
  • 语法: 
unset 变量名
  • 示例
unset MYENV

5. 获取环境变量的值并进行操作

5.1 使用 cut 命令
  • 功能:从环境变量中提取特定部分。

  • 语法

    env | grep 变量名 | cut -d'=' -f2
  • 示例

    env | grep PATH | cut -d'=' -f2
5.2 使用 awk 命令
  • 功能:从环境变量中提取特定部分。

  • 语法

    env | awk -F'=' '/变量名/ {print $2}'
  • 示例:

    env | awk -F'=' '/PATH/ {print $2}'

五.main函数的问题 

main函数最多有几个参数?

在 C 语言中,main 函数最多可以接收两个参数。这两个参数分别是:

  1. argc:表示命令行参数的数量,包括程序名称本身。

  2. argv:是一个指向字符指针数组的指针,存储了所有命令行参数的字符串。

例如,main 函数的定义通常如下:

int main(int argc, char *argv[]) {// 程序代码return 0;
}

此外,还有一些扩展形式的 main 函数,例如在某些系统中,main 函数可以接收第三个参数 envp,用于访问环境变量。这种形式的 main 函数定义如下:

int main(int argc, char *argv[], char *envp[]) {// 程序代码return 0;
}

但需要注意的是,这种带有 envp 参数的形式并不是 C 标准的一部分,而是某些编译器或操作系统提供的扩展。在标准 C 中,main 函数的参数最多为两个。

总的来说:main函数最多有3个参数。

我们写的代码变成进程的时候是子进程,是由bash父进程创建的,所以main对应的命令行参数和命令行参数表,环境变量表其实是父进程bash传递给main的,怎么传递的,在我们后面的程序替换/程序加载会聊到。

#include <stdio.h>
#include <string.h>int main(int argc, char* argv[], char* envp[])
{(void)argc;(void)argv;for(int i=0;envp[i];i++){printf("envp[%d]->%s\n", i, envp[i]);}return 0;
}
  • (void)argc;(void)argv;:这两个语句用于告诉编译器argcargv是故意未使用的,避免编译器发出警告。

我们自己导入的环境变量是可以被上面的子进程拿到的,也就是环境变量可以被子进程继承:

环境变量也是可以被往下继承的(孙子进程....),所以所环境变量通常具有全局特性。

六.理解环境变量的特性

6.1环境变量具有全局特性

我们上面的进程间环境变量的继承关系就说到了:环境变量具有全局特性

示例代码:Linux 上的 C 语言程序展示进程间环境变量的继承

以下是一个简单的 C 语言程序,展示如何在父进程中设置环境变量,并在子进程中继承和访问这些环境变量:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {// 父进程定义一个环境变量const char* my_var = "Hello World";setenv("MY_VAR", my_var, 1);// 创建子进程pid_t pid = fork();if (pid == -1) {// 创建子进程失败perror("fork");return 1;} else if (pid == 0) {// 子进程const char* child_var = getenv("MY_VAR");printf("子进程中的 MY_VAR 值为: %s\n", child_var);} else {// 父进程printf("父进程中的 MY_VAR 值为: %s\n", my_var);}return 0;
}
  1. 设置环境变量:使用 setenv 函数在父进程中设置环境变量 MY_VAR,其值为 "Hello World"

  2. 创建子进程:使用 fork 函数创建一个子进程。

  3. 子进程继承环境变量:子进程通过 getenv 函数获取环境变量 MY_VAR 的值,并打印出来。

  4. 父进程输出:父进程直接打印其设置的环境变量 my_var 的值。

运行程序后输出如下:

父进程中的 MY_VAR 值为: Hello World
子进程中的 MY_VAR 值为: Hello World

通过上述代码,我们可以看到子进程成功继承了父进程的环境变量,并且可以在子进程中访问和使用这些环境变量。

6.2补充两个概念:为后面埋伏笔

a.Shell 变量赋值规则

Shell也支持一个变量名等于一个变量值,中间左右两则不能带空格,否则会当成命令而报错:

所以:

在 Shell 中,变量赋值的语法是 变量名=变量值,等号两侧不能有空格。如果变量值包含空格或其他特殊字符,需要用引号包围。例如:

variable_name=value
variable_name='value with spaces'
variable_name="value with spaces"

这种定义出来的变量称为本地变量

我们使用env是看不到我们定义的如上的 i 变量的!!!不是环境变量。

我们可以使用:

set 

显示环境变量和本地变量,也就是说bash会记录两套变量!

所以bash内部其实有3张表,但是我们重点的是上面的两张表!!! 

我们也可以看出:本地变量不会被子进程继承,只能在bash内部被使用!!!

那bash为什么要有本地变量,他又有什么用呢?

其实本地变量我们早就使用过了:

i=0; while [ $i -le 10 ]; do echo $i; let i++; done

就是让bash可以成为一门语言嘛,这样的话就可以定义变量了嘛,就可以用了。(就是要有本地变量的语法支持)

本地变量的作用域限制在当前函数内,不会影响外部环境中的同名变量。

b.导入环境变量(export XXX)是导到谁里面了? 

我们的环境变量是在bash的上下文中的,export这样的命令,他是命令啊!!!在我们bash的子进程去执行export命令的话,export就是一个子进程,子进程导环境变量是把数据放在了父进程bash环境变量表里面,不是说进程之间是有独立性的吗,也就是说父进程bash可以将环境变量交给子进程,但是子进程是没有办法将数据交给父进程的,因为没有继承关系啊,所以export这样的命令一旦被执行,他怎么可能将环境变量导给bash呢?

只有一种解释:export这个命令和其他的命令不一样:

子进程不能修改父进程的环境变量,因为每个进程都有自己的独立内存空间和环境变量表。然而,export 命令在 Bash 中的工作方式有些特殊,它实际上并不是通过子进程来修改父进程的环境变量,而是直接在当前 Shell 环境中操作。让我们详细解释一下这个过程:

export 命令的工作原理:

export 我们称为内建命令(built-in command)是一个 Bash 内置命令,而不是一个外部可执行文件。这意味着当你在 Bash 中执行 export 命令时,它不会创建一个新的子进程,而是在当前的 Bash 环境中直接操作,让bash自己亲自执行,一般都是bash自己调用函数,或者系统调用完成的。因此,export 命令可以直接修改当前 Shell 环境的环境变量表。

在后面谈到进程控制后,我们会进行对应的操作😎 

相关文章:

环境变量

目录 一.概念介绍 1.1命令行参数 二.一个例子&#xff0c;一个环境变量 2.1查看环境变量 2.2如何理解环境变量呢&#xff1f;存储的角度 2.3环境变量最开始从哪里来的呢&#xff1f; 概括&#xff1a; 1. 环境变量的存储 2. 命令查找过程 3. 环境变量表和命令行参数…...

Scale AI 创始人兼 CEO采访

Scale AI 创始人兼 CEO 亚历山大王&#xff08;Alexander Wang&#xff09;首次亮相节目接受采访。他的公司专注于为人工智能工具提供准确标注的数据。早在 2022 年&#xff0c;王成为世界上最年轻的白手起家亿万富翁。 美国在全球人工智能竞赛中的地位&#xff0c;以及它与中…...

MongoDB中常用的几种高可用技术方案及优缺点

MongoDB 的高可用性方案主要依赖于其内置的 副本集 (Replica Set) 和 Sharding 机制。下面是一些常见的高可用性技术方案&#xff1a; 1. 副本集 (Replica Set) 副本集是 MongoDB 提供的主要高可用性解决方案&#xff0c;确保数据在多个节点之间的冗余存储和自动故障恢复。副…...

【Erdas实验教程】001:Erdas2022下载及安装教程

文章目录 一、Erdas2022安装教程1. 安装主程序2. 拷贝补丁3. 安装LicenseServer4. 运行软件 二、Erdas2022下载地址 一、Erdas2022安装教程 Erdas2022全新界面如下&#xff1a; 1. 安装主程序 下载安装包并解压&#xff0c;以管理员身份运行 “setup.exe” 或 “setup.vbs”&…...

Python3 【函数】水平考试:精选试题和答案

Python3 【函数】水平考试&#xff1a;精选试题和答案 Python 函数考试试卷及答案。共计30题&#xff0c;其中选择题15题、填空题10题、编程题5题&#xff0c;试卷满分为100分。 一、选择题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 以下哪个关键字用于定义函数…...

stm8s单片机(三)时钟系统与时钟切换

一个单片机系统要正常运行应包括四个部分: 电源&#xff0c;晶振&#xff0c;复位电路&#xff0c;下载电路。 晶振就是时钟。 stm8有四种时钟源 HSE (High Speed External clock signal)HSE user-ext (High Speed External clock signal user external)HSI (High Speed Inter…...

ChatGPT高效处理图片技巧使用详解

ChatGPT&#xff0c;作为OpenAI开发的预训练语言模型&#xff0c;主要用于生成自然语言文本的任务。然而&#xff0c;通过一些技巧和策略&#xff0c;我们可以将ChatGPT与图像处理模型结合&#xff0c;实现一定程度上的图像优化和处理。本文将详细介绍如何使用ChatGPT高效处理图…...

图漾Halcon版本SDK使用教程【V1.1.0新版本】

文章目录 1.下载并安装 Halcon1.1 下载Halcon软件1.2 安装Halcon 2.下载Camport_Halcon_gentl SDK2.1 下载Camport_Halcon_gentl SDK2.2 Camport Halcon SDK介绍2.3 Halcon SDK环境配置与运行2.3.1 SDK环境配置2.3.2 获取相机支持的参数2.3.3 配置相机参数并运行相机 2.4 遍历H…...

C语言二级

//请编写函数fun()&#xff0c;该函数的功能是&#xff1a;计算并输出给定整数n的所有因 //子&#xff08;不包括1和自身&#xff09;之和。规定n的值不大于1000。例如&#xff0c;在主函数 //中从键盘给n输入的值为856&#xff0c;则输出为&#xff1a;sum 763。 //注意&…...

软工_软件工程

2025.01.24&#xff1a;软件工程导论学习笔记 第2节 软件工程 2.1 软件发展 - 四个阶段2.1.1 程序设计2.1.2 程序系统2.1.3 软件工程2.1.4 第四阶段 2.2 软件危机2.2.1 软件危机 - 定义2.2.2 软件危机 - 主要表现 2.1 软件发展 - 四个阶段 2.1.1 程序设计 软件生产个体化&…...

【creo】CREO配置快捷键方式和默认单位

了解CREO工作目录设置 设置快捷方式启动目录&#xff0c;就能自动加载其中的配置。 一、通过键盘快捷方式 保存配置 creo_parametric_customization.ui 文件&#xff1a; 二、通过映射键录制 通过这种方式可以监听鼠标的点击事件。使用键盘快捷方式无法找到需要的动作时候可…...

go理论知识——Go Channel 笔记 [特殊字符]

go理论知识——Go Channel 笔记 &#x1f4dd; 1. 基本概念 &#x1f9e0; 1.1 Channel 是什么&#xff1f; Channel 是 Go 语言中用于在不同 Goroutine 之间进行通信的机制。Channel 是类型安全的&#xff0c;意味着你只能发送和接收特定类型的数据。 1.2 Channel 的创建 …...

微信小程序压缩图片

由于wx.compressImage(Object object) iOS 仅支持压缩 JPG 格式图片。所以我们需要做一下特殊的处理&#xff1a; 1.获取文件&#xff0c;判断文件是否大于设定的大小 2.如果大于则使用canvas进行绘制&#xff0c;并生成新的图片路径 3.上传图片 async chooseImage() {let …...

第05章 11 动量剖面可视化代码一则

在计算流体力学&#xff08;CFD&#xff09;中&#xff0c;动量剖面&#xff08;Momentum Profiles&#xff09;通常用于描述流体在流动方向上的动量分布。在 VTK 中&#xff0c;可以通过读取速度场数据&#xff0c;并计算和展示动量剖面来可视化呈现速度场信息。 示例代码 以…...

Android Studio 新版本24.2.2 运行后自动切到 LogCat

最近更新了 Android studio 版本&#xff0c;发现有个问题&#xff1a; 每次 Run app 之后。都会自动切换到 run 标签。这让我非常不习惯。我个人习惯在app 运行后查看Logcat 最后靠 deepSeek 找到一种解决方案&#xff1a; Android Studio 中截图如下&#xff1a;...

使用EVE-NG-锐捷实现OSPF

一、OSPF基础知识 Open shortest Path First(OSPF)开放式最短路径优先协议 1.OSPF的关系状态 (1)邻居关系(TWO-WAY) 只发送hello包不发送LSA包(链路状态通告包) (2)邻接关系(FULL) OSPF设备与设备之间相互建立OSPF关系&#xff0c;初始为邻居关系(TWO-WAY)状态&#xff0…...

解决使用Selenium时ChromeDriver版本不匹配问题

在学习Python爬虫过程中如果使用Selenium的时候遇到报错如下session not created: This version of ChromeDriver only supports Chrome version 99… 这说明当前你的chrome驱动版本和浏览器版本不匹配。 例如 SessionNotCreatedException: Message: session not created: This…...

景联文科技加入AIIA联盟数据标注分委会

2025年1月16日&#xff0c;中国人工智能产业发展联盟&#xff08;简称AIIA&#xff09;数据委员会数据标注分委会&#xff08;以下简称“分委会”&#xff09;正式成立。景联文科技成为第一批AIIA联盟数据标注分委会委员单位。 数据标注分委会的成立旨在搭建数据标注领域产学研…...

IoTDB结合Mybatis使用示例(增删查改自定义sql等)

IoTDB时序库是当前越来越流行以及基于其优势各大厂商越来越易接受的国产开源时序数据库&#xff0c;针对IoTDB的内容不做过多介绍&#xff0c;在使用该时序库时&#xff0c;往往有一定入门门槛&#xff0c;不同于关系型数据库或文档型数据库那般方便维护和接入开发&#xff0c;…...

穷举vs暴搜vs深搜vs回溯vs剪枝系列一>解数独

题目&#xff1a; 解析&#xff1a; 部分决策树&#xff1a; 代码设计&剪枝&回溯&#xff1a; 代码&#xff1a; class Solution {private boolean[][] row, col;private boolean[][][] gird; public void solveSudoku(char[][] board) {//下标->数字&#xff…...

C#@符号在string.Format方法中作用

本文详解@符号在string.Format方法中作用。...

mysql 学习2 MYSQL数据模型,mysql内部可以创建多个数据库,一个数据库中有多个表;表是真正放数据的地方,关系型数据库 。

在第一章中安装 &#xff0c;启动mysql80 服务后&#xff0c;连接上了mysql&#xff0c;那么就要 使用 SQL语句来 操作mysql数据库了。那么在学习 SQL语言操作 mysql 数据库 之前&#xff0c;要对于 mysql数据模型有一个了解。 MYSQL数据模型 在下图中 客户端 将 SQL语言&…...

JVM栈溢出线上环境排查

#查看当前Linux系统进程ID、线程ID、CPU占用率&#xff08;-eo后面跟想要展示的列&#xff09; ps H -eo pid,tid,%cpups H -eo pid,tid,%cpu |grep tid #使用java jstack 查看进程id下所有线程id的情况 jstack pid 案例2 通过jstack 排查死锁问题 #启动java代码 jstack 进…...

【vue3组件】【大文件上传】【断点续传】支持文件分块上传,能够在上传过程中暂停、继续上传的组件

一、概述 本示例实现了一个基于 Vue3 和 TypeScript 的断点上传功能。该功能支持文件分块上传&#xff0c;能够在上传过程中暂停、继续上传&#xff0c;并且支持检测已经上传的分块&#xff0c;避免重复上传&#xff0c;提升上传效率。以下是关键的技术点与实现流程&#xff1…...

Kafka运维宝典 (三)- Kafka 最大连接数超出限制问题、连接超时问题、消费者消费时间超过限制问题详细介绍

Kafka运维宝典 &#xff08;三&#xff09; 文章目录 Kafka运维宝典 &#xff08;三&#xff09;一、Kafka Broker 配置中的最大连接数超出限制问题1. 错误原因2. 相关 Kafka 配置参数2.1 connections.max2.2 max.connections.per.ip2.3 num.network.threads2.4 connections.ma…...

【135. 分发糖果 困难】

题目&#xff1a; n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果&#xff0c;计…...

AAAI2024论文解读|HGPROMPT Bridging Homogeneous and Heterogeneous Graphs

论文标题 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning 跨同构异构图的小样本提示学习 论文链接 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning论文下载 论文作者 Xingtong Yu, Yuan…...

算法题(49):反转链表II

审题&#xff1a; 需要我们对指定范围的链表进行反转&#xff0c;并返回反转后链表的头结点 思路&#xff1a; 方法一&#xff1a;vector法 我们先遍历一次链表&#xff0c;并把数据对应的存在数组中&#xff0c;然后利用数组的reverse方法进行反转数据&#xff0c;最后再遍历一…...

代码随想录day20

235. 利用二叉搜索树的特性即可 /** lc appleetcode.cn id235 langcpp** [235] 二叉搜索树的最近公共祖先*/// lc codestart /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) :…...

文档智能扫描,提升无纸化办公效率

随着无纸化办公的推广和移动设备的普及&#xff0c;用户迫切需要将纸质文档快速、准确地转换成电子格式&#xff0c;以提高工作效率和信息管理的便捷性。同时&#xff0c;用户将文档扫描成电子版后&#xff0c;可以自行通过加密和访问控制提高电子文档的安全性&#xff0c;以满…...

差分等长的原理

差分等长是指在设计差分信号传输线路时&#xff0c;保证两条差分线的长度尽量一致&#xff0c;长度之差在一个合理的范围内。这是为了确保两个差分信号时刻保持相反极性&#xff0c;减少共模分量&#xff0c;从而提高信号传输的质量。 在差分信号传输中&#xff0c;两条差分线…...

“““【运用 R 语言里的“predict”函数针对 Cox 模型展开新数据的预测以及推理。】“““

主题与背景 本文主要介绍了如何在R语言中使用predict函数对已拟合的Cox比例风险模型进行新数据的预测和推理。Cox模型是一种常用的生存分析方法&#xff0c;用于评估多个因素对事件发生时间的影响。文章通过具体的代码示例展示了如何使用predict函数的不同参数来获取生存概率和…...

真正理解std::move

std::move的作用只有一个&#xff0c;那就是把一个左值强制转换为右值&#xff0c;有了这个右值&#xff0c;右值允许的任何操作就可以实施了。比如&#xff1a;1. 这个右值可以赋给一个左值变量&#xff0c;2. 这个右值可以被一个右值引用来引用。 class A {public:A(int n):…...

Vue3.5 企业级管理系统实战(三):页面布局及样式处理 (Scss UnoCSS )

本章主要是关于整体页面布局及样式处理&#xff0c;在进行这一章代码前&#xff0c;先将前两章中的示例代码部分删除&#xff08;如Home.vue、About.vue、counter.ts、App.vue中引用等&#xff09; 1 整体页面布局 页面整体布局构成了产品的框架基础&#xff0c;通常涵盖主导…...

Python3 【函数】项目实战:5 个新颖的学习案例

Python3 【函数】项目实战&#xff1a;5 个新颖的学习案例 本文包含5编程学习案例&#xff0c;具体项目如下&#xff1a; 简易聊天机器人待办事项提醒器密码生成器简易文本分析工具简易文件加密解密工具 项目 1&#xff1a;简易聊天机器人 功能描述&#xff1a; 实现一个简易…...

17.Word:李楠-学术期刊❗【29】

目录 题目​ NO1.2.3.4.5 NO6.7.8 NO9.10.11 NO12.13.14.15 NO16 题目 NO1.2.3.4.5 另存为手动/F12Fn光标来到开头位置处→插入→封面→选择花丝→根据样例图片&#xff0c;对应位置填入对应文字 (手动调整即可&#xff09;复制样式&#xff1a;开始→样式对话框→管理…...

基于GS(Gaussian Splatting)的机器人Sim2Real2Sim仿真平台

项目地址&#xff1a;RoboGSim 背景简介 已有的数据采集方法中&#xff0c;遥操作&#xff08;下左&#xff09;是数据质量高&#xff0c;但采集成本高、效率低下&#xff1b;传统仿真流程成本低&#xff08;下右&#xff09;&#xff0c;但真实度&#xff08;如纹理、物理&…...

基于Django的豆瓣影视剧推荐系统的设计与实现

【Django】基于Django的豆瓣影视剧推荐系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用了Python作为后端开发语言&#xff0c;采用Django作为后端架构&#xff0c;结…...

提升企业内部协作的在线知识库架构与实施策略

内容概要 在当前快速变化的商业环境中&#xff0c;企业对于提升内部协作效率的需求愈显迫切。在线知识库作为信息存储与共享的平台&#xff0c;成为了推动企业数字化转型的重要工具。本文将深入探讨如何有效打造与实施在线知识库&#xff0c;强调架构设计、知识资产分类管理及…...

Vuex中的getter和mutation有什么区别

在现代前端开发中&#xff0c;状态管理是一个不可忽视的话题&#xff0c;而Vuex作为Vue.js的官方状态管理库&#xff0c;在大型应用中扮演着至关重要的角色。当我们使用Vuex进行状态管理时&#xff0c;getter和mutation是两个重要的概念。虽然它们都是用来处理状态的&#xff0…...

springboot 动态线程池

在Spring Boot中&#xff0c;可以使用ThreadPoolTaskExecutor类来创建动态线程池。以下是一个示例&#xff1a; 首先&#xff0c;需要在配置文件中配置线程池的属性&#xff0c;例如最小线程数、最大线程数、线程存活时间等。可以在application.properties或application.yml中…...

Android - 通过Logcat Manager简单获取Android手机的Log

由于工作需要&#xff0c;经常需要获取Android手机的Log。 平常都是通过adb命令来获取&#xff0c;每次都要写命令。 偶然的一个机会&#xff0c;我从外网发现了一个工具 Logcat Manager&#xff0c;只需要通过简单的双击即可获取Android的Log&#xff0c;这里也分享一下。 目…...

qt-QtQuick笔记之常见项目类简要介绍

qt-QtQuick笔记之常见项目类简要介绍 code review! 文章目录 qt-QtQuick笔记之常见项目类简要介绍1.QQuickItem2.QQuickRectangle3.QQuickImage4.QQuickText5.QQuickBorderImage6.QQuickTextInput7.QQuickButton8.QQuickSwitch9.QQuickListView10.QQuickGridView11.QQuickPopu…...

C语言【基础篇】之流程控制——掌握三大结构的奥秘

流程控制 &#x1f680;前言&#x1f99c;顺序结构&#x1f4af; 定义&#x1f4af;执行规则 &#x1f31f;选择结构&#x1f4af;if语句&#x1f4af;switch语句&#x1f4af;case穿透规则 &#x1f914;循环结构&#x1f4af;for循环&#x1f4af;while循环&#x1f4af;do -…...

LeetCode100之全排列(46)--Java

1.问题描述 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案 示例1 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例2 输入&#xff1a;nums [0,1] 输出&#xf…...

html、js、css实现爱心效果

好的&#xff01;我们可以进一步美化这个爱心效果&#xff0c;增加更多动态和视觉吸引力。以下是改进后的代码&#xff0c;包括以下功能&#xff1a; 1. 背景渐变&#xff1a;添加动态背景渐变效果。 2. 爱心阴影&#xff1a;为爱心添加阴影&#xff0c;使其更具立体感。 3. 随…...

记录 | 基于Docker Desktop的MaxKB安装

目录 前言一、MaxKBStep 1Step2 二、运行MaxKB更新时间 前言 参考文章&#xff1a;如何利用智谱全模态免费模型&#xff0c;生成大家都喜欢的图、文、视并茂的文章&#xff01; MaxKB的Github下载地址 参考视频&#xff1a;【2025最新MaxKB教程】10分钟学会一键部署本地私人专属…...

信息学奥赛一本通 2110:【例5.1】素数环

【题目链接】 ybt 2110&#xff1a;【例5.1】素数环 【题目考点】 1. 深搜回溯 2. 质数 【解题思路】 1~n的数字构成一个环&#xff0c;要求相邻数字加和必须是质数。 该题最终输出的是一个序列&#xff0c;只不过逻辑上序列最后一个数字的下一个数字就是序列的第一个数字…...

5.1.4 软件工具+开发环境

文章目录 软件工具软件开发环境 软件工具 软件工具是辅助软件工程实施的软件&#xff0c;也叫CASE工具。软件工具可分为支持软件开发过程的工具、软件维护工具、软件管理工具3类。 支持软件开发过程的工具 需求分析工具&#xff1a;从需求定义制定出功能规范&#xff0c;描述软…...

嵌入式知识点总结 Linux驱动 (四)-中断-软硬中断-上下半部-中断响应

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.硬中断&#xff0c;软中断是什么&#xff1f;有什么区别&#xff1f; 2.中断为什么要区分上半部和下半部&#xff1f; 3.中断下半部一般如何实现&#xff1f; 4.linux中断的…...