Linux 环境变量 与 命令行参数

什么是环境变量

  • 从结构上来看,环境变量就是操作系统维护的一组:key-value 的键值对。

不知道你是否有一个疑问:为什么我们写代码编译链接 形成的可执行程序要运行起来需要带路径呢?Linux 内置的命令也是可执行程序,为什么这些命令就不用带路径呢?

  • 我们都知道,Linux 的系统指令是放在 /usr/bin 目录下的。/usr/bin 是系统命令的默认搜索路径,所以执行系统的指令不需要带路径!
  • 也就是说如果我们自己写的可执行程序也想不加路径直接用名称来运行,我们只要将可执行文件移动到 /usr/bin 目录下就可以啦!
    #include<stdio.h>int main()
    {printf("hello environment\n");return 0;
    }
    
    mv test /usr/bin/__test:将形成的 test 可执行文件移动到 /usr/bin 目录,并将文件重命名为 __test,那么这个 __test 程序,就能够不加路径直接运行啦!
    在这里插入图片描述

命令行获取环境变量

除了将可执行文件,放到系统指令的默认搜索路径,还有没有其他的办法呢?因为将可执行文件放到系统命令的默认搜索路径是不优雅的行为!我要是能将可执行文件所在的路径设置为系统命令的搜索路径,那也一样能做到嘛!

PATH 环境变量

事实上,系统命令的默认搜索路径可不止 /usr/bin 一个,可是有好多呢。PATH 环境变量里面全都是呢!那么怎么查看 PATH 环境变量中的内容呢?echo PATH 行嘛?不行的哦!直接 echo PATH 会被解析成字符串的,要打印 PATH 的内容,需要加上 $ 符号
在这里插入图片描述
我们看到,PATH 的内容有如下特征:

  • 由很多的路径组成。
  • 路径与路径之间由冒号隔开。

PATH 不是系统命令的默认搜索路径嘛!那么我们怎么加入自己的目录呢?修改 PATH 内容的本质就是对 PATH 重新赋值
那我们来看看将我们写的可执行文件所在的目录添加到 PATH 环境变量需要

  • 首先我们获可执行文件所在的目录:pwd
    在这里插入图片描述
  • 然后就是将这个目录添加到 PATH 环境变量中。思考🤔:PATH=/root/cppcpp/cpp/12_6_cpp_2 这么写是不是正确的呢?你这么做了,发现我们写的可执行文件也能直接运行,但是,原 PATH 中的内容就不见了!想要新增目录,可以这么写:
    PATH=$PATH:/root/cppcpp/cpp/12_6_cpp_2
    
    可以看到可执行文件所在的目录被添加到了 PATH 环境变量中:在这里插入图片描述
    直接运行可执行程序:__test
    在这里插入图片描述
    我们重新启动 xshell ,然后打印 PATH 环境变量中的内容,发现我们刚才添加的目录消失不见了!这是为什么呢?
  • 这是因为,我们每次启动 xshell 操作系统会在 PATH 环境变量的配置文件中加载,给 PATH 环境变量赋值。这就意味着,如果我们不修改 PATH 环境变量的配置文件,再每次 xshell 重新启动的时候,我们都要重新添加目录!这就非常麻烦!

我们想要一劳永逸的话就要修改 PATH 环境变量的配置文件。

  • 当前用户环境变量的配置文件

    • cd ~:进入当前用户的家目录。
    • ls -al:查看家目录下的所有文件。
      在这里插入图片描述
  • .bashrc.bash_profile 文件修改
    这里面有两个文件:.bashrc.bash_profile。在这两个文件中,我们能够为当前用户配置属于自己的 PATH 环境变量,也就是说配置这两个文件只会对当前用户生效!
    我们可以看到这两个文件中都有一段脚本代码(红框框的部分)。

    • .bash_profile 文件中表示,如果家目录下存在 .bashrc 文件,就会执行这个文件。
    • 同理,在 .bashrc 文件中表示,如果 /etc/bashrc 文件存在那么就执行这个文件。
      在这里插入图片描述

    可以得出结论:环境变量的配置文件有很多,配置文件之间还存在调用关系。
    想要为当前用户配置环境变量,修改这两个文件都可以(我们以修改 .bashrc 文件为例),有两种书写的方式:

    • `export PATH=“$PATH:/root/cpp”
      在这里插入图片描述
    • PATH=$PATH:/root/cppcpp
      export PATH
      在这里插入图片描述
      修改 .bashrc 文件之后,我们可以使用:source .bashrc 刷新一下配置文件,我们就能看到我们通过修改配置文件添加的环境变量了!
      在这里插入图片描述

事实上,xshell 启动之后,操作系统加载环境变量的配置文件的顺序是:

/etc/profile ----> ~/.bash_profile ----> ~/.bashrc ----> /etc/bashrc

这是 centos 的加载顺序,具体的 linux 操作系统可能会有差异,但是原理都是差不多的!

现在我们就能够理解,为什么我们在命令行窗口输入一个命令,如果不存在这个命令,报错的提示是:command not found 了!

  • 这证明,执行一个命令,他会在 PATH 环境变量中搜索,是否存在这个变量!如果不存在,就是因为找不到嘛!
  • 当然,你也可以指定路径运行指令就不用在 PATH 环境变量中搜索了!

HOME 环境变量

这个环境变量的值就是登陆用户的家目录!

  • root 用户:
    在这里插入图片描述

  • 普通用户:
    在这里插入图片描述
    我们登陆 xshell 的时候,为啥默认就是在登录用户的家目录呢?
    事实上,登录 xshell 的时候,xshell 会识别到登录的用户是谁,然后填充 HOME 这个环境变量。登录成功之后,xshell 分配 bash,执行类似 cd 命令,进入登录用户的家目录!

SHELL

这个环境变量可以查看使用的是哪一个 shell

查看所有的环境变量

env

env 命令可以查看 bash 进程从系统继承下来的环境变量。

在这里插入图片描述
可以看到这里面就有几个是我们刚才讲到的,这里在补充几个:

  • HOSTNAME:主机名字。我们在命令行中看到的命令行提示符的一部分就有主机名,他就是根据 HOSTNAME 环境变量获取的,然后打印到显示器上!
    在这里插入图片描述

  • USER:这个环境变量是登录用户的用户名,在命令行提示符中就会用到!

  • PWD:这个环境变量保存的是当前进程所处的目录,bash 不也是个进程嘛!

  • LOGNAME:这个和 USER 可以认为是一样的,你可以通过切换用户来验证。

  • OLDPWD: 这个环境变量保存的是当前进程上一次处于那个目录,这就是 cd - 的原理啦!

  • HITSIZE:我们上下键可以切换我们历史输入的命令,其实历史输入的命令是被保存到 ~/.bash_history 中的!而这个 HITSIZE 就是能够保存历史输入命令的最大条数!
    在这里插入图片描述

函数获取环境变量

我们要是想要在我们自己写的程序中获取环境变量怎么办呢?
我们来看一个函数吧:char* getenv(const char* name) 记得包含头文件:#include<stdlib.h>
通过传入环境变量的 key 获取他的 value
我们可以写一个代码获取 PATH 环境变量

#include<stdio.h>
#include<stdlib.h>
int main()
{printf("PATH=%s\n", getenv("PATH"));return 0;
}

在这里插入图片描述

main 函数参数获取环境变量

在使用 main 函数的参数获取环境变量之前,我们先来学习一个知识点:命令行参数

命令行参数

什么是命令行参数

我们之前其实就一直在用命令行参数,就是不知道叫这个东东罢了!
在这里插入图片描述
这里的 -a -l 选项就是命令行参数啦!

命令行参数是干什么的,有什么用?

命令行参数就是用来给某些函数传参的,这些系统指令是不是也是一个一个的进程!那么他的本质就是可执行程序,比如同一个 ls 命令,带上不同的选项,就能实现不同的效果,不就是通过参数来控制的嘛!带上不同的选项本质就是传入不同的参数!

到后面我们会自己来写一个命令行解释器的程序,那时候你就更能理解了!

大家都知道 main 函数其实也是有参数的,并且 C 语言的入口函数其实也不是 main 函数,而是一个叫做 startup 的函数。这就意味着,main 函数也是被其他函数调用的,那么就能够做到为 main 函数传参!

argc && argv

我们先来看看 main 函数的其中两个参数吧:

#include<stdio.h>int main(int argc, char* argv[])
{return 0;
}
  • argc:整形,表示 argv 数组中的元素个数 。
  • argv:一个 char* 类型的数组。

好的,不管这么多,我们直接打印这个数组来看看是个什么东西:

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

可以看到,argv 中的内容与运行可执行程序时是否带选项,带的选项数量都有关联:
你已经看出来了是吧:就是将运行可执行程序的方式和各种参数当做一个字符串,然后对字符串做分割,将运行可执行程序的方式和各种选项分别当做一个字符串填充到 argv 这个字符串数组中!
在这里插入图片描述
这样做有什么用呢
我们就可以根据可执行程序加上不同的选项,表现出不同的功能啦:
我们根据 argv 的内容和数量做出判断,不同的选项对应不同的功能!

#include<stdio.h>int main(int argc, char* argv[])
{if(argc != 2 || (strcmp(argv[1], "--help") == 0)){printf("%s [-a|-l|-b]\n", argv[0]);}else{if(strcmp(argv[1], "-a") == 0){printf("这是功能一\n");}else if(strcmp(argv[1], "-l") == 0){printf("这是功能二\n");}else if(strcmp(argv[1], "-b") == 0){printf("这是功能三\n");}else{printf("%s [-a|-l|-b]\n", argv[0]);}}return 0;
}

在这里插入图片描述
这是不是就跟 ls 命令有点相似啦!

命令行参数表

命令行参数表就是那个 argv 数组哈,我们给可执行程序带的选项都会被写入命令行参数表!命令函数参数表的结构是这个样子的:
在这里插入图片描述
这是什么意思呢?就是说这个命令行参数表最后一个有效元素的下一个元素一定是 NULL 这个 NULL 是自动设置的!
那么我们就多了一种打印命令行参数的方式啦:

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

在这里插入图片描述

环境变量表获取环境变量

main 函数中可不止 argc argv 这两个参数,还有一个参数是:char* env
这个 env 变量和 argv 变量是一样的!指的是结构是一样的,最后一个有效元素的下一个元素会被自动设置为 NULL。我们也可以直接打印这个环境变量表来看看,究竟是怎么个事儿!

在这里插入图片描述
我们可以看到也是顺利的将环境变量给打出来了!
你可能就会有一个疑问了:
./test 会启动一个进程,在这个进程没有启动之前是没有环境变量的,但是启动这个进程之后为什么就会有环境变量呢?

  • 我们运行的进程,其实都是 bash 的子进程,bash 本身在启动的时候就会从操作系统的配置文件中加载环境变量!环境变量也是数据,子进程当然会继承父进程的数据啦!换句话说,我们定义的环境变量在所有进程中是都可以被看到的,因此环境变量具有全局属性

如何验证环境变量是可以被继承的?

首先要验证这个问题,我们需要为我们的 bash 进程添加一个自定义的环境变量!然后在子进程中获取所有的环境变量,看是否会出现我们自定义的环境变量!
那么怎么为 bash 进程添加环境变量嘞?
我们能在命令行直接定义变量嘛?向下面这样:
在这里插入图片描述
我们看到,这样定义变量用 echo 命令也是可以打出来的!但是我们在 env 中并没有找到我们定义的这个变量!说明这种方式定义的变量并不是环境变量!直接在命令行定义的变量叫做本地变量。这个问题我们等会再说!
想要为 bash 添加环境变量需要加上一个 export,像下面这个样子:
在这里插入图片描述
想要取消一个环境变量可以使用 unset 命令,添加一个环境变量之后,unset 之后就不能查找到这个环境变量啦!
在这里插入图片描述

那我们就可以写代码来验证环境变量是可以被继承的啦:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>int main(int argc, char* argv[], char* env[])
{pid_t id = forrk();if(id == 0){printf("I am child process, i have env: MY_VALUE\n", getenv("MY_VALUE"));}else{sleep(100);}return 0;
}

可以看到子进程能够继承 bash 的环境变量!
在这里插入图片描述
这里有一个问题,如果在子进程中修改了从 bash 进程继承下来的环境变量,会怎么样呢?

  • 环境变量也是数据,如果子进程修改父子进程共享的数据,那么就会发生写时拷贝,这个知识点在进程的创建一文中详细讲解过哦!

你可以在子进程中调用 setenv 函数,修改或者添加环境变量来验证子进程修改环境变量不会影响父进程的环境变量!

本地变量

刚才讲到过本地变量的概念:在命令行直接定义的变量
这个本地变量在本 bash 内部有效,不会被子进程继承(验证方法跟验证环境变量能够被子进程继承是一样的,这里就不在演示了!),这是因为本地变量没有必要继承给子进程!


查看本地变量:set,这个命令可以用来查看本地变量和环境变量!

我们定义了一个本地变量:MY_VALUE 在环境边两个中找不到这个变量,但是在 set 中能够找到!
在这里插入图片描述
另外本地变量也是可以变成环境变量的,你应该都猜到怎么做了~只需要 export 本地变量就能够将本地变量变成环境变量!
可以看到,将刚才定义的本地变量 MY_VALUE export 之后就能在环境变量中查找到啦!
在这里插入图片描述

environ 获取环境变量

在 C 语言中,提供了一个 char** 类型的变量 environ 指向父进程的环境变量表!在使用的时候需要用 extern 声明一下,C 语言的语法嘛!
在这里插入图片描述

内建命令与普通命令

在这里插入图片描述
看上面的现象,我们定义了一个本地变量,通过 echo 能将本地变量打印出来!
我就有一个问题啦:echo 是一个命令吧,我们之前说过所有的命令都是 bash 的子进程,我们又知道,MY_VALUE 是一个本地变量,不能被子进程继承,那么,echo 命令是如何拿到 MY_VALUE 变量,并把他打印出来的呢?

  • 如果你看过我之前写的文章,你就知道啦,其实并不是所有的命令都是 bash 的子进程,就比如这个 echo 命令,他并不是 bash 的子进程,而是 bash 这个进程自己来执行的!因此 echo 命令既能够打印出来环境变量也能够打印出来本地变量!

我们将那些不用创建子进程,而由 bash 进程来执行的命令称为内建命令
我们将那些需要创建子进程,有子进程来执行的命令称为普通命令
我们之前使用的 cd 命令也是一个内建命令,可以使用系统调用 chdir 来实现 cd 命令!

知识点:

  1. 环境变量的概念。
  2. 获取与修改环境变量
    • 命令行
    • 函数
  3. 环境变量全局属性的理解。
  4. 本地变量与环境变量。
  5. 内建命令与普通命令。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/205060.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Builder设计模式

我们往往不想一下子从构造方法里面把参数传递过去&#xff0c;而是一个个参数构造。此时就用到Builder设计模式。 比如&#xff1a;属性计算中&#xff0c;我们构造各个方法。

11.AUTOSAR 诊断栈分析(四)--FIM和DCM

目录 1.Event Debounce策略 2.Function Inhibition Manager 3.DCM 4.小结 前面我们用了大概三篇的篇幅描述了诊断栈的基本概念,分别如下: 8.AUTOSAR 诊断栈分析(一)-CSDN博客 9.AUTOSAR 诊断栈分析(二)-CSDN博客

html刷题笔记

1 em 12 pt 16 px 100% source元素为audio、video、picture元素指定多个媒体文件 margin是用来隔开元素与元素的间距&#xff1b;padding是用来隔开元素与内容的间隔。 margin用于布局分开元素使元素与元素互不相干&#xff1b;padding用于元素与内容之间的间隔&#xff0c;…

【前端架构】清洁前端架构

探索前端架构&#xff1a;概述与干净的前端架构相关的一些原则&#xff08;SOLID、KISS、DRY、DDD等&#xff09;。 在我之前的一篇帖子中&#xff0c;我谈到了Signals和仍然缺少的内容[1]。现在&#xff0c;我想谈谈一个更通用的主题&#xff0c;即Clean Frontend Architectu…

Mysql综合案例练习<1>

MySql综合案例练习<1> 题目一题目二题目三题目四题目五题目六题目七题目八题目九题目十题目十一题目十二题目十三题目十四题目十五题目十六题目十七题目十八题目十九 题目一 创建数据库test01_library 创建表 books&#xff0c;表结构如下&#xff1a; CREATE DATABASE …

【js】连续相同请求时,如何中断还未返回的请求

需求 前端页面&#xff0c;用户连续翻页&#xff0c;或是连续筛选这样的连续请求&#xff0c;如果保证发起一次请求时&#xff0c;去中断掉上一次还未返回的请求&#xff1f; 代码 使用axios实现 import axios from axios; import requistDuplicateBlacklist from ./requis…

量子纠缠通讯:未来通讯技术的革命性突破

量子纠缠通讯:未来通讯技术的革命性突破 引言 随着科学技术的不断发展,量子纠缠通讯已成为当今最热门的研究领域之一。作为一种革新性的通讯技术,量子纠缠通讯有望为我们的信息安全和传输速度带来前所未有的提升。那么,究竟什么是量子纠缠通讯,它的原理又是如何的呢?本…

【Hadoop-Distcp】通过Distcp的方式进行两个HDFS集群间的数据迁移

【Hadoop-Distcp】通过Distcp的方式进行两个HDFS集群间的数据迁移 1&#xff09;Distcp 工具简介及参数说明2&#xff09;Shell 脚本 1&#xff09;Distcp 工具简介及参数说明 【Hadoop-Distcp】工具简介及参数说明 2&#xff09;Shell 脚本 应用场景&#xff1a; 两个实时集…

HWL-2511-SS 路由器命令执行漏洞复现 [附POC]

文章目录 HWL-2511-SS 路由器命令执行漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现0x06 修复建议HWL-2511-SS 路由器命令执行漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用文章内的相关技术从事非法测试,…

股市复苏中的明懿金汇:抓住新机遇

2023年对于明懿金汇来说是充满挑战与机遇的一年。面对复杂多变的市场环境&#xff0c;明懿金汇展现了其对市场趋势的敏锐洞察和卓越的策略适应能力。以下是该公司在2023年的主要投资策略和市场适应方式的详细分析。 随着2023年中国股市迎来反弹&#xff0c;明懿金汇迅速调整了…

Linux Docker 安装Nginx

1.21、查看可用的Nginx版本 访问Nginx镜像库地址&#xff1a;https://hub.docker.com/_/nginx 2、拉取指定版本的Nginx镜像 docker pull nginx:latest #安装最新版 docker pull nginx:1.25.3 #安装指定版本的Nginx 3、查看本地镜像 docker images 4、根据镜像创建并运行…

Shopify 开源 WebAssembly 工具链 Ruvy

最近,Spotify 开源了Ruvy,一个 WebAssembly 工具链,能够将 Ruby 代码转换为 Wasm 模块。Ruvy 基于ruby.wasm, 用 Rust 实现,提升了性能并简化了 Wasm 模块的执行。 Ruvy 利用了ruby.wasm提供的 Ruby 解释器模块,并使用wasi-vfs (WASI 虚拟文件系统)将其与所有指定的 Rub…

汽水瓶

某商店规定&#xff1a;三个空汽水瓶可以换一瓶汽水&#xff0c;允许向老板借空汽水瓶&#xff08;但是必须要归还&#xff09;。小张手上有n个空汽水瓶&#xff0c;她想知道自己最多可以喝到多少瓶汽水。 数据范围&#xff1a;输入的正整数满足 1≤n≤100 注意&#xff1a;本…

java.lang.UnsupportedOperationException解决方法

问题描述 在实际开发中经常会有类似的这种代码&#xff0c;想要按类的某一个属性对列表中的元素分组。 例如&#xff1a; 有一些学生&#xff0c;然后根绝他们的年龄对他们进行分组。可以写出如下代码。 public class UnsupportedOperationExceptionDemo {DataNoArgsConstru…

ARM Cortex-M安全之MPU介绍

ARM Cortex-M处理器系列中的存储保护单元&#xff08;Memory Protection Unit&#xff0c;简称MPU&#xff09;是一种硬件机制&#xff0c;可用于在嵌入式系统中实现存储保护和访问权限控制。MPU允许开发人员对不同的内存区域分配不同的权限&#xff0c;从而提供对系统代码和数…

关键字volatile作用和用法

目录 一、多线程编程中的volatile关键字 二、嵌入式编程中的volatile关键字 三、 优化编译器优化 四、 指针类型转换 一个定义为volatile的变量是说这变量可能会被意想不到地改变&#xff0c;这样&#xff0c;编译器就不会去假设这个变量的值了。 精确地说就是&#xff0c;…

动态规划求解完全背包问题(c++实现)

首先是朴素版代码&#xff0c;三层for循环第一层是第几个物品&#xff0c;第二层是体积&#xff0c;第三层是第几个物品的个数&#xff1b;朴素版代码时间复杂度较高&#xff0c;效率低&#xff0c;容易超时。 #include<iostream> using namespace std; const int N1010…

【Docker】Swarm的ingress网络

Docker Swarm Ingress网络是Docker集群中的一种网络模式&#xff0c;它允许在Swarm集群中运行的服务通过一个公共的入口点进行访问。Ingress网络将外部流量路由到Swarm集群中的适当服务&#xff0c;并提供负载均衡和服务发现功能。 在Docker Swarm中&#xff0c;Ingress网络使…

RTL编码(2)——模块优化

一、顶层模块的划分 在RTL编码中&#xff0c;我们是以模块为单位进行设计的&#xff0c;模块之间的连接和嵌套关系对于电路结构有着很大的影响。一个好的系统设计中&#xff0c;我们应该使得模块尽量满足以下两个标准&#xff1a; 顶层模块扁平化内部模块层次化 1.1 顶层模块扁…