【Linux】环境变量及相关指令

一、环境变量的基本概念

其实,我们早就听说过环境变量,比如在学习 JAVA / Python 的时候,会在 Windows 上配置环境变量:

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

1、常见环境变量

  • PATH(当前用户)指定命令的搜索路径
  • HOME指定用户的主工作目录(即用户登陆到 Linux 系统中时,默认的目录)。
  • SHELL:当前 Shell,它的值通常是 /bin/bash
  • HISTSIZE命令历史记录保存数量。(Centos 6.7 默认是 3000 条)

命令 history 可以查看所有命令历史记录。搭配管道 history | wc -l 可以查看命令历史记录的行数。 


2、查看环境变量方法

命令:echo $PATH

PATH 是环境变量的名称,查看指定环境变量。

命令:env

查看系统中所有的环境变量。 


3、环境变量:PATH

我们编写的 C/C++ 代码,编译生成的可执行程序,是不是一个命令呢?

是的。

在 Linux 中,任何一个可执行程序具有可执行权限(x),就称之为它是一条命令。

其实我们听过的:程序、命令、指令、可执行程序等等都是一个概念。

既然是同一个概念,那为什么运行 ls 等命令,不用带 ./ 当前路径,而运行我们自己的可执行程序 proc 必须要带 ./proc 当前路径呢?

环境变量 PATH 中保存的是指定命令的搜索路径。

因为 ls 等命令是在系统的特定路径下保存起来的,而 proc 命令没有保存在 PATH 指定的这些路径中,系统执行命令又只会在 PATH 指定的这些路径中搜索,那么系统就找不到我们的 proc 命令,所以就报错了。

那有没有什么办法,可以让可执行程序 proc 不用带 ./ 当前路径就可以直接运行呢?

方法 1:把 proc 拷贝到环境变量 PATH 指定的任一路径下。

方法 2:把 proc 所在路径添加到环境变量 PATH 中。(注意:自己添加的环境变量,系统重启后是不会被保存的)

$ PATH=$PATH:/home/ll/xxx/10

# :冒号是分隔符
# 注意:PATH=/home/ll/xxx/10,不能这样写,必须加上$符号,否则会把PATH中所有内容覆盖掉

运行效果图:

注意如果我们不幸把环境变量给覆盖掉了,不用担心。环境变量既然是变量,说明它是可以被赋值的,即在用户登录时,通过用户目录下的配置文件赋值的,所以只需要重新登录就好了。

比如:在家目录下的 .bash_profile 文件,这些文件 不建议自己去随意修改

4、环境变量:HOME

分别在 root 和普通用户下执行 cd ~ 和 pwd 查看家目录,分别是 /root 和 /home/xyl,为什么得到的结果不一样呢?

因为不同用户的家目录中的配置文件不一样,所以不同用户下的环境变量 HOME 也是不一样的。

[root@VM-0-12-centos ~]$ echo $HOME
/root
[xyl@VM-0-12-centos ~]$ echo $HOME # 指定用户的主工作目录
/home/xyl

、与环境变量相关的命令

  1. echo显示某个环境变量值。
  2. export设置一个新的环境变量。
  3. env显示所有环境变量。
  4. unset清除环境变量。
  5. set显示本地定义的 shell 变量和环境变量。

1、通过 C 库函数获取环境变量:getenv

(1)getenvsetenv 函数介绍
  • name:环境变量的名称。
  • 返回值:环境变量的内容。
#include <stdlib.h>

char *getenv(const char *name); // 获取环境变量
int setenv(const char *name, const char *value, int overwrite); // 更改或添加环境变量

(2)getenv 使用举例
// proc.c
#include <stdio.h>
#include <stdlib.h> // getenv

int main()
{
    printf("%s\n", getenv("PATH"));
    printf("%s\n", getenv("HOME"));
    printf("%s\n", getenv("SHELL"));
    return 0;
}

运行结果:在 bash 创建的子进程 proc 中成功获取到了环境变量。

结论

  • 我们在命令行上运行的大部分命令,它们的父进程都是 bash
  • bash 创建子进程,然后由子进程执行用户输入的命令。

三、环境变量和本地变量

在命令行中,我们通常可以定义两种变量:

  • 环境变量环境变量通常具有全局属性可以被子进程继承下去)

环境变量实际上是在当前 bash 的上下文中定义的。所以在当前命令行解释器 bash 内可以被访问到,在 bash 创建的子进程内也可以被访问到。


#include <stdio.h>
#include <stdlib.h>

int main()
{
    char* env = getenv("MYENV");
    if(env)
    {
        printf("%s\n", env);
    }
    return 0;
}
直接运行发现没有结果,说明该环境变量根本不存在。
  • 导出环境变量:export MYENV="hello world"
  • 再次运行程序,发现有结果了。说明:环境变量是可以被子进程继承下去的。
  • 本地变量(只能在当前 shell 命令行解释器内被访问,不可以被子进程继承)
[xyl@VM-0-12-centos 10]$ MY_VAL="hello world"  # 定义本地变量(在bash内定义的)

如何查看本地变量呢?

和查看环境变量方式一样:

[ll@VM-0-12-centos 10]$ echo $MY_VAL  # 在当前命令行解释器bash内访问本地变量
hello world
如何证明本地变量不能被子进程继承?
// proc.c
#include <stdio.h>
#include <stdlib.h> // getenv

int main()
{
    printf("%s\n", getenv("MY_VAL"));
    return 0;
}

运行结果:段错误。

分析:

当我们输入 ./proc 后,bash 会创建子进程来执行 proc 程序。但因为本地变量 MY_VAL 只能在当前 bash 内被访问,不能被其子进程继承,所以子进程中的 getenv("MY_VAL") 函数获取不到本地变量 MY_VAL,导致程序报错。

该怎么解决这个问题呢?

可以将本地变量 MY_VAL 设置成环境变量。

[ll@VM-0-12-centos 10]$ exprot MY_VAL  # 把本地变量MY_VAL导出成环境变量

导入成功:

再次运行程序:成功访问到了环境变量 MY_VAL。

上面说到,我们在命令行上运行的大部分命令,都是 bash 创建子进程来执行的,而本地变量不能被子进程继承,那为什么使用 echo 命令,却可以访问本地变量呢?

四、命令行参数

1、main 函数的参数列表

main 函数可以带参数吗?能带几个参数呢?

main 函数可以带参,但大部分都是缺省。

  • argc:命令行参数的个数。
  • argv:字符指针数组(指向各个命令行参数的字符指针所构成的数组)。
int main(int argc, char* argv[]) // 接收命令行参数
{
    for (int i = 0; i < argc; i++)
    {
        printf("argv[%d]: %s\n", i, argv[i]); // 遍历字符指针数组argv
    }
    return 0;
}

运行结果:字符数组中只有一个元素,就是我们输入的命令。

[ll@VM-0-12-centos 10]$ ./proc
argv[0]: ./proc

如果再多输入几个参数,就能观察到如下运行结果:

[ll@VM-0-12-centos 10]$ ./proc agr1 arg2 arg3
argv[0]: ./proc
argv[1]: agr1
argv[2]: arg2
argv[3]: arg3

总结:

实际上我们输入的命令行参数,就是一个个的 C 字符串: "./proc"、"arg1"、"arg2"、"arg3",传给了 main 函数:


2、命令行参数的意义

为什么要存在命令行参数呢?

帮助我们能够给同一个程序,设计出不同的业务功能。

举个小例子,比如我想要实现这样一个计算器:
  • 如果输入 ./cal,则会提示该程序的正确用法:Usage:./cal -[a|s] x y;

  • 输入 ./cal -a 1 2,cal 程序可以输出 1 + 2 的结果;

  • 输入 ./cal -s 4 2,cal 程序可以输出 4 - 2 的结果。

#include <stdio.h>
#include <stdlib.h> // atoi -- 函数原型:int atoi(const char *nptr); // 将C字符串转换成整数
#include <string.h> // strcmp

// cal命令的用法手册
void Usage(const char* cal)
{
    printf("Usage: %s -[a|s] x y\n", cal);
}

int main(int argc, char* argv[]) // 接收命令行参数
{
    // 输入的参数个数不为4
    if (argc != 4)
    {
        Usage(argv[0]);
        return 1; // 退出程序
    }

    // 保存第3个和第4个参数                                                      
    int x = atoi(argv[2]);
    int y = atoi(argv[3]);
    
    // 根据不同参数,执行不同功能,然后输出结果
    if (strcmp(argv[1], "-a") == 0)
    {                                            
        printf("%d + %d = %d\n", x, y, x + y); 
    }
    else if (strcmp(argv[1], "-s") == 0)
    {
        printf("%d - %d = %d\n", x, y, x - y); 
    }
    else
    {
        Usage(argv[0]);
        return 1; // 退出程序
    }

    return 0;
}

运行结果:

[ll@VM-0-12-centos 10]$ ./cal        # 命令使用手册
Usage: ./cal -[a|s] x y
[ll@VM-0-12-centos 10]$ ./cal -a 1 2 # 实现加法
1 + 2 = 3
[ll@VM-0-12-centos 10]$ ./cal -s 4 2 # 实现减法
4 - 2 = 2

3、总结

命令行参数可以让同一个命令,通过带上不同的选项表现出不同的功能和作用。

比如:ls -l、ls -l -a、ls -l -a -i。这就是命令行参数的意义。

我们平常在 VS 中写代码,都知道程序是从 main 函数开始执行,那是谁调用的 main 函数呢?
  • 编程者写的 main 函数被 void mainCRTStartup(void) 函数调用的,这个函数定义在 VS 安装目录的某个 .c 文件中(VS 的版本不同,存放的位置也不同),它会执行一些初始化操作,如从内核中获取命令行参数和环境变量值、初始化全局变量、初始化 IO 等等所需各项准备之后,为调用 main(argc, argv, env) 函数做好了准备。
  • 可以通过 main 函数的参数,可以传递命令行参数和环境变量。

五、环境变量的组织方式

main 函数除了可以传递两个和命令行参数相关的参数 argc 和 argv 以外,还可以传递第 3 个参数 env

int main(int argc, char* argv[], char* env[]);

这也是 main 函数获取环境变量的方式。

通过给 main 函数第三个参数传参,把一个个环境变量传递给当前程序,当前程序运行起来变成进程,就意味着当前这个进程获取到了这些环境变量。

每个被 bash 创建的子进程都会接收到一张环境表环境表是一个字符指针数组,每个指针指向一个以 ’\0’ 结尾的环境字符串(环境变量)。


1、通过 main 的第三个参数获取环境变量

// proc.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[], char* env[]) // 通过第三个参数接收环境变量
{
    for (int i = 0; env[i]; i++) // 循环结束条件为env[i],遍历到NULL停止
    {
        printf("env[%d]: %s\n", i, env[i]); // 遍历字符指针数组env
    }
    return 0;
}

运行结果:获取到了当前 bash 内的所有环境变量。(因为环境变量被 bash 创建的子进程 proc 继承下去了)

所以现在知道 C 库函数 getenv 的实现原理了,比如 printf("%s\n", getenv("PATH"));,其实就是通过在字符指针数组 env 中进行字符匹配,找到 "PATH" 后面的内容并返回。


2、通过全局变量 environ 获取环境变量(了解)

C/C++ 提供了一个全局二级指针变量 char** environ,指向存放环境变量地址的字符指针数组 char* env[ ]。

#include <stdio.h>

int main()
{
    extern char** environ;
    for (int i = 0; environ[i]; i++)
    {
        printf("%s\n", environ[i]); // 等价于 *(environ + i)
    }
    return 0;
}

注意:因为 libc 中定义的全局变量 environ 指向环境变量表,environ 没有包含在任何头文件中,所以在使用时要用 extern 声明。


下面程序运行会报错吗?

不会。

#include <stdio.h>

void show()
{
    printf("hello show\n");
}

int main()
{
    show(10, 20);
    return 0;
}

分析:10 和 20 这两个参数是传给了 show 函数,它们被压入了 show 函数栈帧中,实际上在 show 函数中是可以通过某些指针操作来获取到 10 和 20 的,只是比较复杂。

上述程序中的二级指针 environ 可以通过某种方式在 main 的压栈结构中指向传入的命令行参数 char* env[ ](环境变量表),来获取到环境变量。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ //练习 7.3 修改7.1.1节(第229页)的交易处理程序,令其使用这些成员。

C Primer&#xff08;第5版&#xff09; 练习 7.3 练习 7.3 修改7.1.1节&#xff08;第229页&#xff09;的交易处理程序&#xff0c;令其使用这些成员。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /********************…

CCF编程能力等级认证GESP—C++2级—20231209

CCF编程能力等级认证GESP—C2级—20231209 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)小杨做题小杨的 H 字矩阵 参考答案单选题判断题编程题1编程题2 …

【Apache Superset】从概述、安装到运用,一篇掌握!

文章目录 什么是 Superset&#xff1f;Superset 的优势安装安装 Anaconda 包管理工具创建 Superset 的 Python 环境 下载 Superset 依赖更新工具安装 Superset配置 MySQL 元数据库初始化 Superset 数据库报错一报错二报错三数据库初始化完成 初始化 Superset报错一警告信息 启动…

【解决(几乎)任何机器学习问题】:超参数优化篇(超详细)

这篇文章相当长&#xff0c;您可以添加至收藏夹&#xff0c;以便在后续有空时候悠闲地阅读。 有了优秀的模型&#xff0c;就有了优化超参数以获得最佳得分模型的难题。那么&#xff0c;什么是超参数优化呢&#xff1f;假设您的机器学习项⽬有⼀个简单的流程。有⼀个数据集&…

【算法设计与分析】反转链表 ||

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表…

【MySQL】外键约束的删除和更新总结

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-7niJLSFaPo0wso60 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

【Linux 02】权限基本概念

文章目录 &#x1f308; Ⅰ 权限概念&#x1f308; Ⅱ 权限管理1. 文件访问者分类 (角色)2. 文件类型和访问权限 (事物属性)3. 文件权限值表示方法 &#x1f308; Ⅲ 权限修改1. chmod 设置文件访问权限2. chown 修改文件拥有者3. chgrp 修改文件或目录的所属组 &#x1f308; …

Hive——企业调优经验

前言 本篇文章主要整理hive-3.1.2版本的企业调优经验&#xff0c;有误请指出~ 一、性能评估和优化 1.1 Explain查询计划 使用explain命令可以分析查询计划&#xff0c;查看计划中的资源消耗情况&#xff0c;定位潜在的性能问题&#xff0c;并进行相应的优化。 explain执行计划…

LabVIEW荧光显微镜下微管运动仿真系统开发

LabVIEW荧光显微镜下微管运动仿真系统开发 在生物医学研究中&#xff0c;对微管运动的观察和分析至关重要。介绍了一个基于LabVIEW的仿真系统&#xff0c;模拟荧光显微镜下微管的运动过程。该系统提供了一个高效、可靠的工具&#xff0c;用于研究微管与运动蛋白&#xff08;如…

汉诺塔问题——递归算法与非递归算法

一、问题描述 汉诺塔问题是一个经典的问题。汉诺塔&#xff08;Hanoi Tower&#xff09;&#xff0c;又称河内塔&#xff0c;源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令…

Spring 用法学习总结(一)之基于 XML 注入属性

百度网盘&#xff1a; &#x1f449; Spring学习书籍链接 Spring学习 1 Spring框架概述2 Spring容器3 基于XML方式创建对象4 基于XML方式注入属性4.1 通过set方法注入属性4.2 通过构造器注入属性4.3 使用p命名空间注入属性4.4 注入bean与自动装配4.5 注入集合4.6 注入外部属性…

auto.js教程(autojs教程、autox.js、autoxjs)笔记(二)环境搭建——2、安卓手机投屏软件scrcpy的安装和使用(scrcpy教程)

参考文章&#xff1a;【自动化技术】Autojs从入门到精通 参考文章&#xff1a;AutoXJS开发入门简介菜鸟教程 参考文章&#xff1a;关于Auto.js的下架说明 参考文章&#xff1a;Auto.js 4.1.0 文档 文章目录 005--【环境搭建】2、安卓手机投屏软件scrcpy的安装和使用scrcpy官…

【1024】我的创作纪念日

机缘 1024天了&#xff0c;开始在这里学习编程知识、IT技能&#xff0c;CSDN让我发现了一群热爱学习和分享的小伙伴&#xff0c;也逐渐在这里稳定下来。 收获 不知不觉已经两年多过去了&#xff0c;通过不断的分享&#xff0c;不仅自己的知识技能得到了提升&#xff0c;能帮…

腾讯云4核8G服务器多少钱?

腾讯云4核8G服务器多少钱&#xff1f;轻量应用服务器4核8G12M带宽一年446元、646元15个月&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;标准型SA2服务器1444.8元一年&#xff0c;在txy.wiki可以查询详细配置和精准报价…

SpringCloud-Hystrix:服务熔断与服务降级

8. Hystrix&#xff1a;服务熔断 分布式系统面临的问题 复杂分布式体系结构中的应用程序有数十个依赖关系&#xff0c;每个依赖关系在某些时候将不可避免失败&#xff01; 8.1 服务雪崩 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C&#xff0c;微服…

B2科目二考试项目笔记

B2科目二考试项目笔记 1 桩考1.1 右起点倒库1.2 移库&#xff08;左→右&#xff09;1.3 驶向左起点1.4 左起点倒库1.5 驶向右起点 2 侧方停车考试阶段&#xff08;从路边开始&#xff09;&#xff1a; 3 直角转弯4 坡道定点停车和起步5 单边桥6 通过限速限宽门7 曲线行驶8 连续…

[数学建模] 计算差分方程的收敛点

[数学建模] 计算差分方程的收敛点 差分方程&#xff1a;差分方程描述的是在离散时间下系统状态之间的关系。与微分方程不同&#xff0c;差分方程处理的是在不同时间点上系统状态的变化。通常用来模拟动态系统&#xff0c;如在离散时间点上更新状态并预测未来状态。 收敛点&…

Selenium图表自动化开篇

目录 前言&#xff1a; 使用 Canvas 或者 SVG 渲染 选择哪种渲染器 代码触发 ECharts 中组件的行为 前言&#xff1a; 图表自动化一直以来是自动化测试中的痛点&#xff0c;也是难点&#xff0c;痛点在于目前越来越多公司开始构建自己的BI报表平台但是没有合适的自动化测试…

计算机设计大赛 深度学习OCR中文识别 - opencv python

文章目录 0 前言1 课题背景2 实现效果3 文本区域检测网络-CTPN4 文本识别网络-CRNN5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习OCR中文识别系统 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;…

使用bpmn-js 配置颜色

本篇文章介绍如何使用bpmn-js给图例配置颜色。该示例展示了如何向BPMN图添加颜色的多种不同方法。 通过层叠设置颜色 这种方式比较简单&#xff0c;直接通过设置图片的CSS层叠样式就可实现。 .highlight-overlay {background-color: green; /* color elements as green */opa…