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,一经查实,立即删除!

相关文章

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 …

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

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

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

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、根据镜像创建并运行…

java.lang.UnsupportedOperationException解决方法

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

关键字volatile作用和用法

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

【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 顶层模块扁…

系列学习前端之第 3 章:一文精通 css

全套学习 HTMLCSSJavaScript 代码和笔记请下载网盘的资料&#xff1a; 链接: 百度网盘 请输入提取码 提取码: 6666 一、CSS基础 1. CSS简介 CSS 的全称为&#xff1a;层叠样式表 ( Cascading Style Sheets ) 。 CSS 也是一种标记语言&#xff0c;用于给 HTML 结构设…

使用arcpy移除遥感影像云层

先讲思路&#xff0c;然后上代码&#xff1a; 去除云层 思路1&#xff1a; 如果同一地理区域的多个图像&#xff0c;其中一些部分有丰富的云&#xff0c;而另一些部分没有云&#xff0c;则可以将它们组合起来&#xff0c;以便无云的部分替代多云的部分。这种方法很简单&…

C++ vector基本操作

目录 一、介绍 二、定义 三、迭代器 四、容量操作 1、size 2、capacity 3、empty 4、resize 5、reserve 总结&#xff08;扩容机制&#xff09; 五、增删查改 1、push_back & pop_back 2、find 3、insert 4、erase 5、swap 6、operator[] 一、介绍 vector…

前端CSS(层叠样式表)总结

CSS2总结 一、CSS基础 1. CSS简介 CSS 的全称为&#xff1a;层叠样式表 ( Cascading Style Sheets ) 。CSS 也是一种标记语言&#xff0c;用于给 HTML 结构设置样式&#xff0c;例如&#xff1a;文字大小、颜色、元素宽高等等。 简单理解&#xff1a; CSS 可以美化…

<软考>软件设计师-3程序设计语言基础(总结)

(一) 程序设计语言概述 1 程序设计语言的基本概念 1-1 程序设计语言的目的 程序设计语言是为了书写计算机程序而人为设计的符号语言&#xff0c;用于对计算过程进行描述、组织和推导。 1-2 程序语言分类 低级语言 : 机器语言&#xff08;计算机硬件只能识别0和1的指令序列)&…

java设计模式学习之【装饰器模式】

文章目录 引言装饰器模式简介定义与用途实现方式 使用场景优势与劣势装饰器模式在Spring中的应用画图示例代码地址 引言 在日常生活中&#xff0c;我们常常对基本事物添加额外的装饰以增强其功能或美观。例如&#xff0c;给手机加一个保护壳来提升其防护能力&#xff0c;或者在…

SwiftUI 中创建一个自定义文件管理器只需4步!你敢信!?

概览 在 SwiftUI 中写一个自定义文件内容的管理器有多难呢&#xff1f; 答案可能超乎小伙伴们的想象&#xff1a;仅需4步&#xff01;可谓是超级简单&#xff01; 在本篇博文中&#xff0c;您将学到如下内容&#xff1a; 概览1. 第一步&#xff1a;定义文件类型2. 第二步&…

Dockerfile 指令的最佳实践

这些建议旨在帮助您创建一个高效且可维护的Dockerfile。 一、FROM 尽可能使用当前的官方镜像作为镜像的基础。Docker推荐Alpine镜像&#xff0c;因为它受到严格控制&#xff0c;体积小&#xff08;目前不到6 MB&#xff09;&#xff0c;同时仍然是一个完整的Linux发行版。 FR…

从主从复制到哨兵模式(含Redis.config配置模板)

文章目录 前言一、主从复制1.概述2.作用3.模拟实践搭建场景模拟实践 二、哨兵模式1.概述2.配置使用3.优缺点4.sentinel.conf完整配置 总结 前言 从主从复制到哨兵模式。 一、主从复制 1.概述 主从复制&#xff0c;是指将一台 Redis 服务器的数据&#xff0c;复制到其他的 Red…