乖乖娃 發表於 2025-10-14 00:00:00

Linux环境变量详解与实战

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>前言</li><li>一、基本概念<ul class="second_class_ul"><li>1.1、命令行参数</li></ul></li><li>二、常见的环境变量<ul class="second_class_ul"></ul></li><li>三、查看环境变量的方法<ul class="second_class_ul"></ul></li><li>四、环境变量相关的命令<ul class="second_class_ul"></ul></li><li>五、 环境变量的组织方式<ul class="second_class_ul"></ul></li><li>六、通过代码如何获取环境变量<ul class="second_class_ul"></ul></li><li>七、通过系统调用获取或设置环境变量<ul class="second_class_ul"></ul></li><li>八、环境变量通常是具有全局属性的<ul class="second_class_ul"></ul></li><li>Linux 环境变量怎么退出<ul class="second_class_ul"></ul></li><li>总结<ul class="second_class_ul"></ul></li></ul></div><p class="maodian"></p><h2>前言</h2>
<p>本章节我们来看看环境变量和命令行参数是什么。我想大家可能会有疑惑,我们写的程序运行时需要 &quot; ./ &quot;。但是我们 ls、cd 等命令却不需要。这就和我们环境变量有关了。我们一起来看看环境变量是什么吧。</p>
<p class="maodian"></p><h2>一、基本概念</h2>
<p>环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。</p>
<p>如:我们在编写 C/C++ 代码的时候,在链接的时候,从来不知道我们的所链接的动静态库在哪里但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。</p>
<p>环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。</p>
<p class="maodian"></p><h3>1.1、命令行参数</h3>
<p>众所周知,我们在编写代码时,一定都会创建一个 main 函数,既然 main 是函数,那它有参数吗,参数是什么?答案当然是有的,一个是 argc,还有一个 argv;</p>
<div class="dxycode"><pre class="brush:cpp;">#include&lt;stdio.h&gt;
int main(int argc, char* argv[])
{
    for(int i = 0; i &lt; argc; i++)
    {
      printf("argv[%d]: %s\n", i, argv);
    }
    return 0;
}</pre></div>
<p>我们的 main 函数是我们自己程序的入口,但是我们的 main 函数其实也是得被调用的。第一个执行的函数并不是 main 函数。我们的 argv 就是一个指针数组,而 argc 就是记录 argv 里面元素的个数。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AG0I1.png" width="800" /></p>
<p>当我们直接 ./ 运行时是这个样子,也就是我们程序的名字。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AG0939.png" width="800" /></p>
<p>我们可以看出来我们的 argv 其实就是把我们命令行以空格为分隔符,把它们一个一个的变成指针数组的样子。</p>
<p>实际上我们的 argv 就是一个指针数组,当我们在命令行输入 ./code 或者以空格作为分隔符,其实我们输入的就是一个长字符串。我们在执行某些 C 语言命令时,这个字符串就会以空格作为分隔符切成多份。将我们第一个字符串的地址填到 argv 的位置,以此类推&hellip;,有效个数就是我们的 argc。这个东西就叫做命令行参数。我们 argv 把有效元素放完一般以 NULL 结尾。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AG0434.png" width="300" /></p>
<p>我们可以发现,我们平时使用的各种命令,其实就是各种可执行程序,像 ls -a -l 等,可以在我们的命令中带很多的选项。</p>
<p>我们可以来看一下这个代码。</p>
<div class="dxycode"><pre class="brush:cpp;">#include&lt;stdio.h&gt;
#include&lt;string.h&gt;
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
      printf("Usage: %s [-a|-b|-c]\n", argv);
      return 0;
      //代码在执行时一定要带-a|-b|-c选项;
    }
    const char* arg = argv;
    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);
    return 0;
}</pre></div>
<p>我们运行后可以看到:</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AG03G.png" width="600" /></p>
<p>所以说 main 函数的命令参数的作用就是我们的程序可以通过选项实现不同的子功能。这也就是为什么我们的之前指令的子功能为什么有选项的原因。我们获得的字符串其实是先被 bash 先获得后将我们的字符串切分了,然后构建 argv 后依次放入。</p>
<p>在我们的进程启动时,我们的进程会拥有一张 argv 表,用来支持实现选项功能。</p>
<p class="maodian"></p><h2>二、常见的环境变量</h2>
<p>但是为什么我们执行我们的命令时要加入 ./,而系统的却不需要呢?</p>
<p>我们如果要执行一个程序时,我们一定要先找到它!所以我们运行自己的程序时要加 ./ 表示它在当前的路径下。为什么系统不需要呢?原因是我们的系统指令有环境变量来帮助我们找到二进制文件。也就是说如果我们把我们的程序拷贝到我们系统的命令上就不需要带 ./ 了。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AG1435.png" width="800" /></p>
<p>我们将我们的程序拷配到上面的目录。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AG63B.png" width="800" /></p>
<p>我们就不需要 ./ 了。但是我们最好不要这么做,原因是我们的代码没有经过测试,也没有经过时间的验证等等,代码很有可能有 bug,放到系统的默认路径下就有可能污染系统。</p>
<p>但是新的问题是,系统为什么就认识 /usr/bin 路径下的地址呢,系统为什么就会在这个地址上去查呢?</p>
<p>原因就是环境变量:PATH。这个环境变量表明的是我们系统要去什么地方找二进制文件。</p>
<p>whoami:查询当前用户是谁的环境变量。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AG64A.png" width="800" /></p>
<p>HOME:当前用户家目录。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AGAG.png" width="800" /></p>
<p>当我们输入 cd ~ 时会默认回到我们的家目录就是靠这个环境变量。</p>
<p>SHELL:用户登录时用的是什么版本的 shell,将 bash 的路径保存起来了。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AGC56.png" width="800" /></p>
<p>USER:代表当前用户是谁。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AGHP.png" width="800" /></p>
<p>LOGNAME:当前登录的用户是谁。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AGM39.png" width="800" /></p>
<p>HISTSIZE:记录历史命令的最多条数。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH2438.png" width="800" /></p>
<p>我们可以输入指令history来查历史命令。</p>
<p>HOSTNAME:当前主机的主机名。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH2226.png" width="800" /></p>
<p>LS_COLORS:配色方案。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH2L7.png" width="800" /></p>
<p>PWD:bash记录我们当前工作路径是什么。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH34W.png" width="800" /></p>
<p>OLDPWD:记录我们上一个路径地址,方便我们用cd-随时切换</p>
<p><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH3463.png" width="800" /></p>
<p>环境变量是由bash使用,我们用户间接使用的。</p>
<p class="maodian"></p><h2>三、查看环境变量的方法</h2>
<p>在系统中查看所有的环境变量一般就用 env 指令。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH3G9.png" width="800" /></p>
<p>环境变量是一个变量,它的构成是名字 + 内容。我们想看一个环境变量可以输入 echo $ 环境变量名来获取环境变量的内容。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH4561.png" width="800" /></p>
<p>这里可以看到,我们的 PATH 变量由路径构成,路径和路径之间用 &quot; : &quot; 分隔,也就是说我们的 OS 在找进程时默认在这几个路径中查找。OS 在找比如 ls 命令时,先在 PATH 的第一个路径找,没有找到再在第二个路径找,以此类推,如果都没有,那就会返回没有找到。所以当我们的 code 拷贝到这几个路径中,code 也就可以直接运行了。除了这种方法,我们还可以试着添加一个新路径到 PATH,这样是不是也可以达到直接运行的效果呢?由于我们的环境变量也是变量,所以我们试试直接赋值 PATH= 路径。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH4504.png" width="800" /></p>
<p>我们可以看到,我们确实可以直接运行我们的程序了,但是这个时候我们再运行别的系统自带指令时发现用不了了。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH4334.png" width="800" /></p>
<p>我们来看看 PATH。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH4557.png" width="800" /></p>
<p>我们可以看到我们的 PATH 给覆盖掉了,这个时候我们不要慌,我们可以直接重启一下 xshell 就可以复原了。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH41J.png" width="800" /></p>
<p>我们来看看添加的指令而不是覆盖的指令,我们可以使用 PATH=$PATH:路径。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH4251.png" width="800" /></p>
<p>从存储的角度来说,当我们登录的时候系统就会给我们创建一个 bash 进程,bash就会从我们系统中读取我们的环境变量的信息。在 bash 进程内部形成一张表,就是环境变量表,环境变量表就是一个指针数组。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH5236.png" width="800" /></p>
<p>当我们在命令行输入 ls -a 时我们的命令行字符串不是先被子进程拿到,而是 bash 进程先拿到,然后将其拆分放到命令行参数表中将其解析,再在环境变量表中找到 PATH 变量去寻找,如果在,就创建子进程去执行,如果不在就显示没有找到。</p>
<p>所以说在 bash 内部有两个表,一个环境变量表,一个命令行参数表。</p>
<p>我们的环境变量表本质就是一个 &lt;key,value&gt; 的长字符串,bash 在启动时,它会想办法在自己的内部创建一个空间,再给每个环境变量创建一个空间形成一个二维数组,然后将环境变量字符串依次拷贝到这个表里面,所以 bash 就在内部维护了这张表。</p>
<p>我们的环境变量最开始是从我们的配置文件中来的,我们 Linux 系统存在着相关的配置文件,这些文件包含了我们所有的环境变量的直接或间接的内容。我们的 bash 在启动时就会从我们的配置文件中读取我们所有的环境变量的值并创建环境变量表。我们可以看到我们系统在家目录中有两个隐藏文件。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH5U3.png" width="800" /></p>
<p>我们打开 .bashrc 可以看到。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHBG.png" width="800" /></p>
<p>这就是系统环境变量的值。而 .profile 可以看到。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHD95.png" width="800" /></p>
<p>系统登录的时候就会要求我们用户去加载 .bashrc 文件,而.bashrc则是要求用户去加载 /etc/bashrc。在 bashrc 文件中的所有东西都是和环境变量有关的和各种配置文件,这也就是为什么我们在更改 PATH 后重启 xshell 会恢复的原因,就是配置文件没有更改,如果更改了配置文件,那下次启动时就不会恢复原样了。</p>
<p class="maodian"></p><h2>四、环境变量相关的命令</h2>
<p>export:在环境变量表中新添一条环境变量,export 环境变量名=值;</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHA33.png" width="800" /></p>
<p>unset:取消环境变量列表中的环境变量,unset 环境变量名;</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH6354.png" width="800" /></p>
<p class="maodian"></p><h2>五、 环境变量的组织方式</h2>
<p>每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以 &rsquo;\0&rsquo; 结尾的环境字符串。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AH6315.png" width="800" /></p>
<p class="maodian"></p><h2>六、通过代码如何获取环境变量</h2>
<p><strong>方法一:</strong></p>
<p>我们前面说了 main 函数可以有参数,但是最多能有几个参数呢,其实 main 函数最多能有 3 个参数,除了 argc 和 argv 这两个,还有一个 char* env[],这个就是父进程传递给我们的环境变量表(最后一个元素也是以 NULL 结尾的),同时 bash 还会把我们的命令行参数表也传给子进程。我们来试着遍历一下我们的环境变量。</p>
<div class="dxycode"><pre class="brush:cpp;">#include&lt;stdio.h&gt;
int main(int argc, char* argv[], char* env[])
{
    (void) argc;
    (void) argv;
    for(int i = 0; env; i++)
    {
      printf("env[%d]-&gt; %s\n", i, env);
      //env最后会运行到结尾,结尾是NULL;
    }
    return 0;
}</pre></div>
<p>可以看到。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHC28.png" width="800" /></p>
<p>我们用代码的方式打印出了环境变量了。</p>
<p>我们获取的这个环境变量是父进程的环境变量,我们用 export 导入环境变量进入 bash 里面,我们的子进程能够看到这些导入的环境变量。</p>
<p>env 输出的环境变量:</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHJ61.png" width="800" /></p>
<p>我们的函数输出的环境变量:</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHN00.png" width="800" /></p>
<p>所以我们的环境变量可以被子进程继承,也就进而可以被孙子进程继承以此类推,所以我们的环境变量在系统中通常具有全局特性。</p>
<p>但是为什么系统要这样设置呢,我们接着往下看。</p>
<p><strong>方法二:getenv</strong></p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHLX.png" width="600" /></p>
<p>这个函数可以根据环境变量的名字获取环境变量内容。成功了就会获取环境变量的起始地址,否则就返回 NULL。</p>
<div class="dxycode"><pre class="brush:cpp;">#include&lt;stdio.h&gt;
#include&lt;stdlib.h&gt;
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-&gt; %s\n", value);
    return 0;
}</pre></div>
<p>运行可以看到。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHO12.png" width="800" /></p>
<p>我们就拿到了 PATH 的内容。</p>
<p>如果我们想要有一个程序只能自己运行不能别人运行就可以用到我们的环境变量(使用 USER 的变量在 if 中,如果满足我的名字才能运行,不然就无法进入 if 语句)。</p>
<p>这里就可以解释为什么要让子进程继承我们的环境变量了,因为这样设计就可以让我们的子进程进行个性化操作,比如定制一个只能让自己运行的程序。</p>
<p class="maodian"></p><h2>七、通过系统调用获取或设置环境变量</h2>
<p><strong>方法三:environ</strong></p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHX52.png" width="400" /></p>
<p>这个environ就会指向我们的环境变量表。</p>
<div class="dxycode"><pre class="brush:cpp;">#include&lt;stdio.h&gt;
#include&lt;unistd.h&gt;
extern char** environ;
//要使用这个函数得先定义
int main(int argc, char* argv[])
{
    (void) argc;
    (void) argv;
    //防止系统报错
    for(int i = 0; environ; i++)
    {
      printf("environ[%d]-&gt; %s\n", i, environ);
    }
    return 0;
}</pre></div>
<p>我们再来试着运行一下。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHVJ.png" width="800" /></p>
<p>我们照样能拿到我们的环境变量。</p>
<p class="maodian"></p><h2>八、环境变量通常是具有全局属性的</h2>
<p><strong>补充:</strong></p>
<p>我们除了环境变量,还有本地变量,所谓的本地变量是指不会被子进程继承,只在 bash 内部使用的变量,我们可以直接用名字 = 值来设置。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHWM.png" width="600" /></p>
<p>我们查看的方式不是 env 了,env 只能看到环境变量,而是使用 set,set 既可以看到环境变量,也可以看到本地变量。</p>
<p style="text-align:center"><img alt="" src="https://zhuji.jb51.net/uploads/allimg/20251014/2-2510141AHR40.png" width="800" /></p>
<p>本地变量具有很多用途,可以用于脚本语言等。删除也是 unset 来删除的。</p>
<p>我们的 export 本质上是一个进程,也就是 bash 的子进程,但是我们的进程之间不是有独立性吗,我们的父进程可以传递给子进程,但是我们的子进程怎么把我们的环境变量传给父进程呢,他们没有继承关系啊。只能有一种解释,那就是我们的 export 进程和普通的进程不一样,我们把这个进程的命令称之为内建命令(built-in command) ,这个命令不需要创建子进程,而是让 bash 自己亲自执行,一般都是 bash 自己调用系统调用搞定的。内建命令还有 pwd,cd 等。</p>
<p class="maodian"></p><h2>Linux 环境变量怎么退出</h2>
<p>通俗的讲,环境变量就是告诉电脑 (实际是操作系统)几个目录。这几个目录下存储几个执行文件,如前面显示的/usr/bin目录,大部分的系统命令都在这个目录下。</p>
<p>当我们输入命令mkdir时,系统就会在环境变量所代表的几个目录从前到后去查找,哪个里面有mkdir文件,然后去执行mkdir命令。</p>
<p>系统中环境变量的名字是PATH,其内容可通过下面的命令显示 (根据操作系统不同和配置不同,略有差别,但格式是统一的,:分割的一堆路径)</p>
<div class="dxycode"><pre class="brush:bash;">$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin</pre></div>
<p>所以如果我们想让自己的命令能被系统找到,就需要把命令所在的目录加到环境变量里面,怎么操作呢?</p>
<p>加到环境变量的路径必须是全路径,全路径指以/开头或以~开头的路径。</p>
<div class="dxycode"><pre class="brush:bash;"># 注意第一个PATH不含, 第二个PATH有符号
$ export PATH=$PATH:/home/ct
$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/ct</pre></div>
<p>所以在以后安装了新的软件,或者写了新的脚本后,都把软件的可执行程序和可执行的脚本所在的目录,加到环境变量里面就可以了。</p>
<p>但是,在命令行中执行export,对环境变量所做的修改,只对当前终端有效,退出后就无效了。为了使得这一操作长期有效,我们需要把这句话写入一个文件中,一个登陆服务器就会被自动读取的文件中。</p>
<p>对于普通用户,在远程登录终端时,家目录下的~/.bash_profile (不是~/.bashrc)会自动被读取,所以我们需要把export语句加入到这个文件中。</p>
<div class="dxycode"><pre class="brush:bash;"># 这是我的~/.bash_profile中的内容,主要是最后一行。可以连续的加入多个路径。
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
export PATH=$PATH:/home/ct:/home/bin:/home/soft/bowtie2/bin</pre></div>
<p>前面提到,系统查找命令的顺序是从环境变量的第一个目录到最后一个目录,在第一次碰到查询的命令后,就调用执行。假如系统存在一个python命令,我们自己又安装了一个python (假如在/home/ct/anaconda/bin目录下),如果我们想执行自己的python程序,就需要把/home/ct/anaconda/bin写在$PATH前面,如下</p>
<div class="dxycode"><pre class="brush:bash;"># 注意$PATH的顺序
$ export PATH=/home/ct/anaconda/bin:$PATH</pre></div>
<p>至此,我们可以熟练使用环境变量来简化命令的输入过程了,因为如果没有环境变量,我们就得需要运行/home/ct/anaconda/bin/python来运行python命令了</p>
<p class="maodian"></p><h2>总结</h2>
<p>以上便是我们环境变量的全部内容了,了解了环境变量,我们就可以使用环境变量去做一些有意思的事情,就比如不让别人运行自己的代码之类的。大家可以自己去尝试,去加深环境变量的用法。</p>
<p>以上就是【Linux系列】Linux 环境变量详解与实战:让你的命令行如虎添翼的详细内容,更多相关资料请阅读琼殿技术社区其它文章!</p>
頁: [1]
查看完整版本: Linux环境变量详解与实战