Linux下的环境变量
一、前言环境变量是一种非常重要的概念它对于系统的行为和应用程序的运行起着关键的作用。那么环境变量究竟在扮演着什么样的角色呢为什么我们在编写C/C代码的时候不需要显式的告诉编译器我们所需要的动静态库在那但链接照样可以成功的生成可执行程序其实这里就有环境变量的辅助所以接下来就让我们一起来学习 -----环境变量二、什么是环境变量首先我们在聊【环境变量】之前知道它是什么环境变量(environment variables)一般是指在操作系统在开机的时候帮我们维护系统运行时的一些动态参数读者可以回忆一下我们在学习Java / Python的时候你们的老师是否有让你们配置过一个东西叫做【环境变量】呢本文我们要讲的其实就是这个打开【高级系统设置】中的【环境变量】我们就可以看到熟悉的一幕了如果有配置过Java中JDK的话一定会对这个有所印象接下去我们要谈的是有关 ---运行一个程序的方式在Windows中我们去运行一个程序是通过双击的形式在Linux中我们去运行一个程序是通过./的形式那你是否想过在Linux中为什么我们不能直接通过敲入这个可执行程序的名字来运行这个程序呢看到下面这二者的区别可以看到当我们去直接执行这个程序的时候系统就报出了command not found。还记得我们在讲 Linux 基本指令 的时候提到过【相对路径】和【绝对路径】那对于./来说呢指的就是【相对路径】那换句话说呢既然我们可以使用【相对路径】来执行程序也可以使用绝对路径去访问到这个程序 即执行/home/XAS/110/环境变量/mytest这句指令即可。我们看到照样是可以正常运行的 那有同学就诧异了为什么一定要带上路径呢为什么我直接运行就不行呢因为在我们的Linux系统中是存在一个东西叫做【环境变量】的这个环境变量可以帮助我们在系统特定的路径下找到那些指定的程序例如我们在使用指令[ls]的时候也是有这个【环境变量】的缘故才得以使我们能够正常地去运行这个指令因为对于[ls]来说你也可以将其看做是一个程序它的存放路径是/usr/bin/那么一直在说这个环境变量那它到底长什么样呢我们来看看常见的环境变量PATH这个【环境变量】定义了系统在执行命令时查找可执行文件的路径。当你输入一个命令时系统会在 PATH 中指定的路径下查找是否存在该命令的可执行文件如果存在则执行该命令。如果 PATH 中没有包含命令的可执行文件路径则系统会提示“command not found”。完美符合上面的情景HOME这个【环境变量】指定了当前用户的主工作目录也就是用户登录系统后所处的默认目录。当用户执行 cd 命令时如果没有指定目标目录则系统会切换到 HOME 目录。SHELL这个【环境变量】定义了当前用户所使用的 Shell。Shell 是用户与操作系统之间进行交互的界面它解释并执行用户输入的命令。通常情况下SHELL 的值为/bin/bash表示用户正在使用 Bash Shell。其他常见的 Shell 包括 /bin/sh、/bin/zsh 等。查看环境变量如果要去查看这个【环境变量】的话就需要使用到我们之前所学习过的一个指令叫做echo其主要是用来【用于打印字符或者回显】在其后面更上一个$PATH我们就可以去查询到当前系统中所有的环境变量这个$呢可以看作是我们在C语言中学习过的指针那时我们通过*去对指针所指向的内容进行解引用的操作在这里也可以这样去理解echo $PATH那我们运行来看一下因为系统中的环境变量有很多所以我们这里是以[:]来进行分割刚才我们所执行的【ls】指令就是在这个/usr/bin这个路径下的 所以我们就可以回答上面的那个问题了为什么 mytest 不可以ls 却可以直接运行呢我们默认的程序在系统中会存在一个环境变量这个环境变量能够帮我们在系统中特定的路径下搜索这个特定的命令。因为当前的这个mytest程序并不在系统的环境变量目录下所以我们想要运行这个程序的就需要手动地将这个程序添加到环境变量的目录下三、如何添加环境变量那要怎么去将一个系统路径添加到【环境变量】中呢这里要使用到一个关键字叫做export然后将我们所要添加的路径放到其中即可export PATH/路径在将这个路径添加到环境变量之后我们再去直接执行这个程序就可以发现它可以像正常的程序一样运行了。但是呢当我在运行其它指令的时候却发现它们都无法执行了这时就有同学疑惑了这是为什么那此时我们再去查看一下环境变量就可以发现之前这里的很多系统路径都不见了而是换成了我们之前所添加进去的这个路径。所以谜底就可以揭晓了我们在往环境变量里添加路径的时候对里面的内容造成了覆盖的情况就像我们之前在讲输入重定向的时候是追加操作但却会造成覆盖的情况此时肯定有老铁已经慌了那之前的指令不能用怎么办呀莫慌大家只需要关掉 Xshell或虚拟机重新启动即可恢复正常哦所以我们在往环境变量里面添加路径的时候不应该形成覆盖这里我再介绍一种方法export PATH$PATH:/路径那我们按照这个方法来运行的话再去查看环境变量就可以发现这个路径被添加进去了而且并没有覆盖掉其他的内容接下去我们再去执行这个程序的时候可以正常运行了同时其它指令也可以运行但是呢当我们再去将服务器重启之后再度运行这个可执行程序的时候便可以发现我们之前添加的路径不见了。那这就又造成了很多同学的困扰这该怎么办呢那此时我灵机一动又想到了一个好的办法那就是直接将这个路径拷贝到环境变量中不过这个算是系统级别的操作我们需要在【root】的权限下进行操作普通用户的话带上sudo即可。然后我们看到在usr/bin目录下就多出个mytest此时再去执行一下这个程序的话就可以发现可以正常运行此时我们可以尝试再去一台服务器发现这个可执行程序依旧是在的说明这个方法是奏效的既然可以拷贝过去的话也可以删除但若是程序不在这个目录下的话就又无法运行咯~四、如何获取环境变量环境变量相关的命令echo:显示某个环境变量值export:设置一个新的环境变量env:显示所有环境变量unset:清除环境变量set:显示本地定义的shell变量和环境变量第一个echo、export我们在上面讲到添加环境变量的时候就谈到了接下去我们来讲讲这个env。可以看到在敲出这个命令之后我们发现系统中所有的环境变量都显示出来了这里可以带读者来简单地看两个对于【HOSTNAME】而言指的就是主机名对于【SHELL】而言就是当我们在执行Linux指令的时候所需要交互的shell外壳在这里是bash最后的【USER】呢指的就是我们当前所登录的用户测试HOME与USER在这里我们可以来测试一下在不同用户之下所看到的环境变量有何不同可以看到在普通用户下去查看HOME的时候出现的就是当前用户的家目录如果是【root】的话出现的便是超级用户的目录对于USER来说也是同理所以环境变量呢是针对特定的人在特定的场景下被使用的其会随着登录用户的不同而变化环境变量的获取方式相信有大部分的读者都学习过C语言那么对于main函数和指针一定有所了解① envp表的介绍 首先的话我想问一个问题main函数的参数可以有几个呢那可能很多同学都不知道怎么回答函数大家都有接触过可以带的参数并没有多大的限制但是对于main函数的话可能大家都是不带参数或者直接写个void代表空参那这里就要给读者普及一下了对于main函数而言我们是可以携带【0】、【2】或【3】个参数的。即下面的三种形式int main(void)int main(int argc, char *argv[])int main(int argc, char *argv[], char *envp[])头两种形式大家或多或少见到过不过本文我们重点要讲解的则是第三种形式也就是这个带三个参数的我们重点关注的是envp[]---这个指针数组如果对这一块知识点还不是很熟悉的同学可以去看看 指针数组详解对于这个envp它里面所存放的都是一个个指针这些指针都指向了一个个的字符串即它的众多有效内容指向一个字符串但是第一个无效内容必须去指向NULL所以我们也可以称之为【表结构】② 通过命令行获取环境变量经过上面这一番的学习我们可以利用main函数中的这第三个参数去获取到系统中的所有环境变量并且打印出来#include stdio.h // char* argc[] 和 envp[] 都是指针数组 int main(int argc, char *argv[], char *envp[]) { int i 0; while(envp[i]!NULL) { printf(envp[%d]:%s\n,i,argv[i]); i; } return 0; }去打印一下看看就可以发现得到了我们想要的结果③ 通过函数获取环境变量接下去第二种方法乃是通过一个函数来获取不过我们在这里就只是获取指定的那个环境变量而不是全部的环境变量。我们依旧使用【man】手册来查看一下这里也要注意包一下此头文件stdlib.h同样我们来看看代码该如何去进行书写这里的USER我们在上面看到过了就是当前所登录的用户所以若是其不为NULL的话我们就可以将其给打印出来#include stdio.h #include stdlib.h int main(void) { char* user getenv(USER); if(user NULL) perror(getenv fail); else printf(USER: %s\n, user); return 0; }那除了USER之外呢我们还知道有一种环境变量叫做PWD就是获取当前所在路径int main(void) { char* pwd getenv(PWD); if(pwd NULL) perror(getenv fail); else printf(%s\n, pwd); return 0; }然后我们再将这个可执行程序加入到【环境变量】中然后再去任何的路径下执行就发现其和pwd指令的效果是同样的了好接下去我们再来做一个提升既然有【环境变量】这个东西那我们就要利用好它来做一个只允许指定用户来运行当前的程序废话不多说我们直接通过代码来看看在这里我们涉及到名称的比较所以我们需要使用到C语言中的strcmp这里记得要包含头文件哦#include stdio.h #include string.h #include stdlib.h #define NAME XAS int main(void) { char* own getenv(USER); if(own NULL) perror(getenv fail); else if(strcmp(own, NAME) 0) printf(程序正常执行\n); else printf(抱歉当前用户 %s 没有权限执行此程序\n, own); return 0; }那因为这里我们需要指定用户来运行所以在这里我将切换为root但是呢有一点很奇怪的是即使我们已经切换到了root的这个账户之下但是为什么我在限定了只有XAS这个用户才可以访问之外root也可以来进行访问呢右侧的话我其实已经给到提示了问题出在这个su上读者有必要知道一下它们的区别su命令:仅切换用户身份不改变环境变量。环境变量继承原用户的环境,如HOME、SHELL等。效果相当于以其他用户身份登录系统。su - 命令:不仅切换用户身份还改变环境变量。环境变量从新登录该用户时的环境变量重新获取。效果相当于重新登录系统。我们可以通过查看PATH环境变量的方式来观察一下于是就可以看到此还是XAS用户所在的环境变量因此经过上面的学习我们知道了要真正地去改变当前系统的环境变量时尤其是我们在切换到root用户的时候我们要使用到su -才可以起到真正的切换我们来看看最后的执行结果可以发现使用su -切换到root用户下的时候在执行这个可执行程序时出现了不一样的结果就是因为当前用户已经完完全全转为了root所以我们在进行比较的时候就进入了第三个分支一样我们可以通过查看环境变量PATH和USER来检测一下发现确实发生了变化最后再来补充一点也就是我们上面谈到过的有关环境变量的命令unset首先可以看到当我们设置了一个环境变量myval后通过[echo]的方式去进行打印此时我们就看到了这个变量所对应的属性接下去呢我通过unset清除掉了这个环境变量此时再去通过[echo]的方式打印时就看不到内容了再来看一点我又使用export将其放入环境变量表中此时我们通过[env]配合[grep]指令去显示所有环境变量的时候就看到这个变量通过键值对的形式进行展现四、详谈环境变量深入理解环境变量表在上面我们有谈到过对于每个用户而言都有属于自己的环境的环境变量表在环境变量表中的每一个都有各自的用途有进行路径查找的有进行身份验证的有进行动态库查找的有用来确认当前路径的环境变量在启动的时候就已经在内存中所以它是一张内存级的表那么环境变量所对应的数据是从哪里来的呢答是从系统的相关配置文件中读取进来的首先我们可以来看看当前家目录下的.bashrc这个文件其主要是用来设置Bash shell的一些环境变量和别名还有一个的话是.bash_profile其和.bashrc类似也是存放一些系统自带的环境变量还有的话则是在根目录下的etc配置文件下的bashrc这个文件这里面所存放的便是当我们在启动系统的时候会自动加载的一些配置项例如说像这里就是我们在前面所说到的【环境变量表】中的路径像这个的话就是我们在 Linux 权限 一文中所提到的umash掩码对于普通用户而言默认为002这些都是当系统在一加载的时候就会自动进行配置的包括的话像我们在切换使用【超级用户】和【普通用户】的时候看到它们的命令提示符并不相同仔细观察可以发现【超级用户】为#而【普通用户】的话则为$。对于这些内容的话也是通过系统启动的时候会自动去进行配置的命令行参数的意义好最后的话我们再来拓展一个东西叫做【命令行参数】好最后的话我们再来拓展一个东西叫做【命令行参数】这个的话我们上面在讲环境变量的获取方式时有所提及即这个argc和argv首先我来说一下它们分别是什么意思argc是一个整数表示命令行参数的数量包括程序名本身就是说argc也是argv这个数组里元素个数。char *argv[]是一个字符数组argv是一个指向字符指针数组的指针每个指针指向一个以 null 结尾的字符串表示一个命令行参数。int main(int argc, char* argv[])在知道它们分别是什么意思后呢我们就可以通过代码的形式展现出它们的作用那此时我们便可以通过这两个参数去获取到所执行命令行中的相关内容想必在通过以上的展示后你就可以初步地感受到命令行参数的魅力所在了但是呢光就这么去看的话还不是很人性化我们可以考虑再做一个菜单选项让用户去进行对应的选择或者呢让用户一定要输入至少2个命令选项否则的话就直接终止程序void Usage(const char* name) { printf(\nUsage: %s -[a|b|c]\n, name); exit(0); } int main(int argc, char* argv[]) { if(argc ! 2) Usage(argv[0]); if(strcmp(argv[1], -a) 0) printf(打印当前目录下的文件名\n); else if(strcmp(argv[1], -b) 0) printf(打印当前目录下文件的详细信息\n); else if(strcmp(argv[1], -c) 0) printf(打印当前目录下的文件名(包含隐藏文件)\n); else printf(其他功能, 待开发\n); }运行之后可以看到当我们直接去运行这个程序的时候因为没有添加参数所以程序直接终止了而且打印出了可供选择的菜单项接下去在我添加上这个命令选项参数的时候在选择了对应的参数之后就打印出了对应的内容那有同学问说了这么久还不是只能在Linux下运行吗Windows下可不行哦这一块的话读者可以去Windows下的【命令提示符】简称cmd看看键入下面这句话看看shutdown /?于是呢我们就可以看到也是会出现许多的命令行选项供我们选择每一个选项都对应着不同的含义具体地读者可以自己去试试看五、总结与提炼首先在开篇我们提到了环境变量的基本概念知道了如果我们要去运行一个程序的话就需要将其路径添加到环境变量中当系统的环境变量中有了这个路径之后当我们在执行此程序时它就可以去识别到路径下有这个内容那如何添加呢通过export PATH$PATH:/路径的方式即可.再者我们就要考虑到如何去通过代码如何 获取到这些环境变量首先对于环境变量相关的命令大家要知晓其中echo、export、env这些是会频繁使用。并且呢很重要的一点是对于不同用户来说环境变量是会不同的会随着用户的登入而自动为其配置环境变量在了解了基本的概念之后我们就可以通过代码去获取到相关的环境变量分别是有命令行第三个参数、通过函数获取其中大家重点要掌握的是第三个方式学会使用getenv()这个方法去获取到相关的环境变量但是呢光就这么去获取的话我们还无法深入地理解到【环境变量】的含义于是我们开始去了解一些配置文件看到了很多在我们启动系统时就会进行配置的内容于是更加进一步地了解到了确实有【环境变量】这个东西的存在。不仅如此我们还通过shell所执行的命令去观察因为这些命令都是shell的子进程所以当它执行命令的时候 就相当于是父进程在执行子进程。那通过执行的结果我们可以看出shell将自己的环境变量表交给了执行的子进程继而它可以去获取到表中对应的内容我的博客即将同步至腾讯云开发者社区邀请大家一同入驻https://cloud.tencent.com/developer/support-plan?invite_code1k823hes1vc