Linux 下命令行参数和环境变量

Linux 下命令行参数和环境变量

  • 命令行参数
    • 为什么要有命令行参数
    • 谁可以做到
    • 结论
  • 环境变量
    • 一些现象
    • 查看环境变量
    • 添加环境变量
      • 添加内存级环境变量
      • 永久有效
    • 其他环境变量
      • HOME
      • PWD
      • SHELL
      • HISTSIZE
    • 自定义环境变量
      • 定义
      • 取消
    • 本地变量
    • 整体理解环境变量
      • 环境变量的组织方式
      • Linux 代码获取环境变量
        • 方式一
        • 方式二
        • 方式三
      • 理解
      • 测试本地变量和环境变量

命令行参数

我们平时写 C 语言代码时, main() 函数是必须要写的;虽然我们一般并不会给 main() 函数带上参数,但 main() 函数的确是 有参数的

int main(int argc, char* argv[])
{return 0;
}

先来解释:
argv 是个 指针数组 ,它本质上是一个 数组 ,里面都是 char* 类型的指针
argc 是指 argv元素的个数

咱们先来做一次测试,看看 argv 里到底有什么 :

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

编译为 mytest 可执行文件,在 命令行 上运行如下指令:

./mytest
./mytest -a
./mytest -a -b
./mytest -a -b -c
./mytest -a -b -c -d

结果如下:

[root@localhost command_line_parameter]$ ./mytest
argv[0] -> ./mytest
[root@localhost command_line_parameter]$ ./mytest -a
argv[0] -> ./mytest
argv[1] -> -a
[root@localhost command_line_parameter]$ ./mytest -a -b
argv[0] -> ./mytest
argv[1] -> -a
argv[2] -> -b
[root@localhost command_line_parameter]$ ./mytest -a -b -c
argv[0] -> ./mytest
argv[1] -> -a
argv[2] -> -b
argv[3] -> -c

一目了然 argv 里面是什么,显然是将输入的指令字符串以空格为界限切开,分别存进 argv 元素所指的空间里,但不论有几个元素, argv 结尾的元素都是 null

切割原理也很简单,无非就是将 空格 替换为 '\0' 即可,如此一个长字符串就变为多个子串

那这有什么意思呢?
请用上面的方法验证下面的代码:

// test.c 文件
#include <stdio.h>
#include <unistd.h>
#include <string.h>int main(int argc, char* argv[])
{if (argc != 2){printf("Usage: %s -[a, b, c, d]\n", argv[0]);return 1;}if (strcmp(argv[1], "-a") == 0)printf("It is function1\n");else if (strcmp(argv[1], "-b") == 0)printf("It is function2\n");else if (strcmp(argv[1], "-c") == 0)printf("It is function3\n");else if (strcmp(argv[1], "-d") == 0)printf("It is function4\n");else printf("Nothing!!!\n");return 0;
}

验证结果是否感觉有些似曾相识?没错,就是指令后带选项罢了,类似 ls -l 等等

为什么要有命令行参数

命令行参数本质是 交给程序不同的选项,从而定制出不同的功能;就像是 Linux 的指令携带有自己的选项一样

目前遇到的许多命令,都是 C 语言写的

谁可以做到

验证下面的代码就得知:父进程的数据,默认能被子进程看到并访问

#include <stdio.h>
#include <unistd.h>
#include <string.h>int g_val = 100;int main()
{printf("It is a process, pid: %d, ppid: %d, g_val: %d\n", getpid(), getppid(), g_val);sleep(5);pid_t id = fork();if (id == 0){// childwhile (1){printf("It is a child process, pid: %d, ppid: %d, g_val: %d\n", getpid(), getppid(), g_val);sleep(1);}}else {// parentwhile (1){printf("It is a parent process, pid: %d, ppid: %d, g_val: %d\n", getpid(), getppid(), g_val);sleep(1);}}return 0;
}

此时我们观察第一行打印数据,其父进程,查询后会发现就是 bash 进程,也就是说, bash 进程是所有在命令行下启动的进程的父进程

所以我们平时输入的指令字符串是默认交给父进程 bash 的(命令行解释器)

所以 argv 里头的指针是 bash 的功劳啦,由于 子进程可以访问父进程的数据,那也就可以将我们输入的指令字符串切割后传入 argv 里让子进程访问到

结论

命令行参数是 C 语言支持让我们给 main() 函数传参的方式,支持我们写可变选项的程序,使同一个程序定制出不同的功能,就类似 Linux 里带选项的指令(百分之七十的指令都是 C 语言实现)

环境变量

环境变量(environment variables)一般是指在 OS 中用来指定 OS 运行环境的一些参数,如:我们在编写 C/C++ 代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关 环境变量 帮助编译器进行查找

一些现象

通过上面的实验,虽然已经可以为我们的程序带上选项,但 Linux 系统指令似乎并不用带上路径,类似 pwdls 等等,而我们的程序却要带上路径才能运行,成为进程,这是为什么呢?

其实 pwd 也有路径,不信的话,下面两句指令是一样的:

/usr/bin/pwd
pwd

指令可以不带路径是因为系统可以找到它呀 ^ ^

在 Linux 当中,存在一些 全局的设置,告诉命令行解释器,应该去哪些路径下去寻找可执行程序

而这个全局的设置是指: PATH ( Linux 环境变量的一个)

指定命令的搜索路径

查看环境变量

echo $PATH

结果就是:

/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/exercise/.local/bin:/home/exercise/bin

系统中有很多配置在我们登录 Linux 系统的时候,就已经被加载到了 bash 进程中(内存),其中就包括 PATH

bash 在执行命令的时候,需要先找到命令,因为是要提前加载的 ,所以 bash 内部维护了一批路径在环境变量里,而这些路径以 : 作为分隔符;
要执行哪个指令就会依次在这些路径下寻找,如果没找到,就会报 command not found 错误;如果找到了,就会加载并运行此程序

而上面 pwd 指令路径就在 /usr/bin/ 下,在 PATH 是存在的,所以不需要手动添加路径

注意:
上面查到的 PATH 默认是 内存级的环境变量,也就是说,一旦我们将 PATH 修改,而 系统配置文件不变,下一次登录时, PATH 值会恢复至原样

添加环境变量

添加内存级环境变量

如果我想自己写的程序也可以像指令一样直接被运行,就需要将此程序的路径添加至环境变量,使得 bash 可以找得到此程序,仔细阅读以下命令

[exercise@localhost envir_var]$ ./mytest
Usage: ./mytest - [a, b, c, d]
[exercise@localhost envir_var]$ ./mytest -a
It is function1
[exercise@localhost envir_var]$ ./mytest -b
It is function2
[exercise@localhost envir_var]$ pwd
/home/exercise/learn/linux-exer/process/envir_var
[exercise@localhost envir_var]$ PATH=$PATH:/home/exercise/learn/linux-exer/process/envir_var
[exercise@localhost envir_var]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/exercise/.local/bin:/home/exercise/bin:/home/exercise/learn/linux-exer/process/envir_var
[exercise@localhost envir_var]$ mytest
Usage: mytest - [a, b, c, d]
[exercise@localhost envir_var]$ mytest -a
It is function1
[exercise@localhost envir_var]$ mytest -b
It is function2
[exercise@localhost envir_var]$

发现此时的 mytest 可执行程序已经可以不带路径直接跑咯 ^ ^

也可以使用 which 指令查看验证哦:

which mytest

所以 添加内存级环境变量 为:

PATH=$PATH:你要添加的路径

永久有效

如果你重新打开终端或者重启系统之后,会发现上面添加的环境变量仅仅在当前 bash 有效,这是因为上面添加的是内存级的环境变量嘛,那如何实现永久有效呢?

那就要修改配置文件咯,对于云服务器来说,每次重新登录时,都会为我们创建一个新的 bash 进程,此 bash 进程会将配置文件数据拷贝一份到自己的内存中,其中 就包括 PATH

如何更改呢?在你的家目录下有一个隐藏文件叫做 .bash_profile ,使用 vim 打开就会发现如下内容:

# .bash_profile# Get the aliases and functions
if [ -f ~/.bashrc ]; then. ~/.bashrc
fi# User specific environment and startup programsPATH=$PATH:$HOME/.local/bin:$HOME/binexport PATH

很显然,我们看见了环境变量 PATH ,所以只需要需改此配置文件即可,在 PATH=$PATH:$HOME/.local/bin:$HOME/bin 的后面添加 :你要添加的路径 即可,我这里是

PATH=$PATH:$HOME/.local/bin:$HOME/bin:/home/exercise/learn/linux-exer/process/envir_var

那么步骤如下:

[exercise@localhost envir_var]$ ./mytest
Usage: ./mytest - [a, b, c, d]
[exercise@localhost envir_var]$ ./mytest -a
It is function1
[exercise@localhost envir_var]$ ./mytest -b
It is function2
[exercise@localhost envir_var]$ pwd
/home/exercise/learn/linux-exer/process/envir_var
[exercise@localhost envir_var]$ cd ~
[exercise@localhost ~]$ vim .bash_profile

然后重启一个终端即可

其他环境变量

在 Linux 里可不止 PATH 一种环境变量,如果你要查看系统中所有的环境变量,使用如下指令:

env

结果会非常多,琳琅满目,暂挑几个说:

HOME

指定用户的主工作目录(即用户登陆到 Linux 系统中时,默认的目录)

如果你是 普通用户,那么登录进入 Linux 系统后,所在位置一般都是 用户家目录

[exercise@localhost ~]$ pwd
/home/exercise

如果你是 超管 root ,那你的家目录就在 /root 下:

[root@localhost ~]# pwd
/root

为什么初次登录系统的目录是这个呢?当然是 系统的配置文件决定的 啦,它会为用户配置好系统家目录

[exercise@localhost envir_var]$ echo $HOME
/home/exercise

PWD

这是个有趣的环境变量:

[exercise@localhost envir_var]$ echo $PWD
/home/exercise/learn/linux-exer/process/envir_var
[exercise@localhost envir_var]$ cd ..
[exercise@localhost process]$ echo $PWD
/home/exercise/learn/linux-exer/process

会发现这就是我们当前的工作目录,这就是 pwd 指令为啥知道我们的工作目录呢?

这是因为系统有一个会变化的环境变量,会随着我们路径的变化,动态地将我们的路径记录在此环境变量里

SHELL

当前 Shell,它的值通常是 /bin/bash

机器启动时,会为用户创建一个命令行解释器 shell 提供服务,那 Linux 怎么知道要运行什么 shell 呢?

[exercise@localhost envir_var]$ echo $SHELL
/bin/bash

HISTSIZE

不知道大家是否会喜欢使用上键来翻找被执行过的历史指令,我甚至可以使用 ctrl + r 来搜索被执行过的历史指令,这些就说明我们使用过的历史指令是可以被系统记录下来的,但 Linux 系统总不能一直记录你所使用过的历史指令呀,所以 HISTSIZE 环境变量就表示 可以被记录的最新的指令条数

[exercise@localhost envir_var]$ echo $HISTSIZE
10000

也即是说系统会为我们维护最新的 10000 条指令,我们也可以使用 history 指令来查看被执行过的历史指令

自定义环境变量

定义

格式:

export name=val

例子:

export MY_ENV=HelloWorld

查看:

[exercise@localhost envir_var]$ env | grep MY_ENV
MY_ENV=HelloWorld
[exercise@localhost envir_var]$ echo $MY_ENV
HelloWorld

同样这也是 内存级的环境变量

取消

格式:

unset name 

例子:

unset MY_ENV

本地变量

就是不带 export 定义一个变量,观察以下操作:

[exercise@localhost envir_var]$ MY_VAR=123456
[exercise@localhost envir_var]$ env | grep MY_VAR
[exercise@localhost envir_var]$ echo $MY_VAR
123456
[exercise@localhost envir_var]$

会发现在 环境变量 里查不到 MY_VAR ,但可以 echo 出来,这就是 本地变量

整体理解环境变量

系统会把与登录相关的,与用户相关的,与路径相关的,与程序相关的等等,所有这些周边的,在系统中设置好的全局的变量就叫做环境变量

查看 所有环境变量 就是:

env

查看单个环境变量 XXX 就是:

echo $XXX

环境变量的组织方式

bash 内部会维护一张 环境变量表,本质就是个 字符指针数组,其内元素指向一个以 '\0' 结尾的字符串,最后一个元素指向 NULL
在这里插入图片描述

Linux 代码获取环境变量

方式一
#include <stdio.h>
#include <unistd.h>int main()
{// 其他头文件里的全局变量 environ(指针数组),需要 extern 声明extern char** environ;for (int i = 0; environ[i]; ++i)printf("env[%d] -> %s\n", i, environ[i]);return 0;
}

编译运行后观察结果,和 env 指令结果本质上是一样的,只是格式不一样

环境变量默认是可以被子进程拿到的,而环境变量默认是在 bash 内部,上面的程序运行为进程后会从 父进程 bash 里看到全局的环境变量并读取

bash 进程启动的时候,默认会给我们子进程形成两张表:

  • 命令行参数表 argv[] (从用户输入的命令行来)
  • 环境变量表 env[] (从 OS 配置文件来)

bash 会通过各种方式交给子进程,而把环境变量给子进程的一种方式就是此 二级指针

extern char** environ;

environ 没有包含在任何头文件中,所以在使用时 要用 extern 声明
其最后的末尾元素是 NULL 指针,而前面的元素内容也已被上面的代码所打印出来

方式二

这种方式和命令行参数很像,就是 main() 函数接收参数啦:

int main(int argc, char* argv[], char* env[])
{return 0;
}

也就可以像下面这样获取:

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

子进程也可以通过 getenv() 函数获取指定环境变量:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main(int argc, char* argv[], char* env[])
{char* path = getenv("PATH");if (path == NULL) return 1;else printf("PATH = %s\n", path);return 0;
}

理解

环境变量具有系统级的全局属性,因为环境变量本身会被子进程继承下去

如果你在命令行启动一个进程 A ,那它的父进程是 bash ,自然可以继承 bash 的环境变量;但如果进程A 也启动一个子进程,此时也可以继承 A 的环境变量,如此继承下去

但有个地方不对劲,就是利用 export 指令自定义环境变量:
export 是指令吧?执行它不是要创建子进程吗?如果创建子进程,那子进程修改环境变量对于 bash 来说是不相干的呀,因为进程是具有独立性的

这是因为 export内建命令,Linux 大部分指令确实需要创建子进程来完成执行,但少部分指令却是由 bash 亲自执行的,再例如 echo 指令,这就是内建命令,暂且可以将其理解为一个函数或者是类的调用即可

也就是说系统对 内建命令 的执行是不需要 PATH 指路的,毕竟都不需要开启新的子进程,不信咱就可以把 PATH 设为空串试一下,试完重新登录即可

测试本地变量和环境变量

我们上面提到过 本地变量,那 本地变量 到底可以用来干啥呢?

看下面的一串指令:

[exercise@localhost envir_var]$ env | grep HELLO
[exercise@localhost envir_var]$ echo $HELLO[exercise@localhost envir_var]$ HELLO=123436
[exercise@localhost envir_var]$ env | grep HELLO
[exercise@localhost envir_var]$ echo $HELLO
123436
[exercise@localhost envir_var]$ export HELLO
[exercise@localhost envir_var]$ env | grep HELLO
HELLO=123436
[exercise@localhost envir_var]$ echo $HELLO
123436
[exercise@localhost envir_var]$

本地变量 还没有被 export 时,env 是查不到的,但 export 后却可以

而这 本地变量 是只在本 bash 内部有效,无法被子进程继承下去,导成 环境变量 才能被继承

这也可以解释 echo 为什么可以打印出 本地变量,这是因为 echo内建命令,由 bash 亲自执行,都不需要继承,获取自己内部的 本地变量 不是易如反掌吗?
而子进程是无法继承 bash 内部的 本地变量,所以 echo 也绝对不是以 bash 创建子进程的方式完成,而是由 bash 亲自执行才可,相互印证

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

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

相关文章

32 增加系统调用(1)

系统调用在 数据手册中的描述 这是在 GDT 中的描述符 这个系统调用 segment selector 指向的时 内核的代码段。因为系统调用需要的权限比较高。 offset 指的时 在内核代码中的具体的函数的地址。

深入浅出消息队列----【Broker 集群】

深入浅出消息队列----【Broker 集群】 单 master多 master多 master 多 slave 异步复制多 master 多 slave 同步复制Dledger 本文仅是文章笔记&#xff0c;整理了原文章中重要的知识点、记录了个人的看法 文章来源&#xff1a;编程导航-鱼皮【yes哥深入浅出消息队列专栏】 Brok…

ssrf攻击fastcgi复现

文章目录 环境搭建使用网页查看开始攻击 环境搭建 在/usr/local/nginx/html下新建一个php文件 phpinfo.php 1.php <?php highlight_file(__FILE__); $url $_GET[url]; $curl curl_init($url); curl_setopt($curl, CURLOPT_HEADER, 0); $responseText curl_exec($curl)…

Neo4J下载安装

Windows 版本 1、 下载链接安装JDK 下载链接 https://download.oracle.com/java/22/latest/jdk-22_windows-x64_bin.msi 下载完毕后默认安装即可 2、 下载Neo4J 进入Neo4j Deployment Center - Graph Database & Analytics下载页面&#xff0c;选择社区版&#xff0c;…

QT Quick QML 实例之定制 TableView

QT Quick QML 实例之定制 TableView 一、演示二、C关键步骤1. beginInsertRows()&#xff08;用户插入行&#xff09;2. roleNames() &#xff08;表格中列映射&#xff09;3. data() &#xff08;用户获取数据&#xff09;4. headerData() &#xff08;表头&#xff09;5. fla…

依靠 VPN 生存——探索 VPN 后利用技术

执行摘要 在这篇博文中,Akamai 研究人员强调了被忽视的 VPN 后利用威胁;也就是说,我们讨论了威胁行为者在入侵 VPN 服务器后可以用来进一步升级入侵的技术。 我们的发现包括影响 Ivanti Connect Secure 和 FortiGate VPN 的几个漏洞。 除了漏洞之外,我们还详细介绍了一组…

ETAS工具链自动化实战指南<二>

----自动化不仅是一种技术&#xff0c;更是一种思维方式&#xff0c;它将帮助我们在快节奏的工作环境中保持领先&#xff01; 目录 往期推荐 RTA-A2L工具概览 RTA-A2L的输出文件 常用命令行参数 场景1&#xff1a;通过 MCSD 文件来生成 .a2l 文件并更新地址 命令用法 命…

比Maven快2~10倍的编译工具mvnd简介与实战

概述 maven-mvnd&#xff0c;可简称&#xff08;或缩写&#xff09;mvnd&#xff0c;the Maven Daemon。Apache Maven团队借鉴Gradle和Takari后开发的更快的构建工具。mvnd内嵌Maven&#xff0c;开发者可无缝从Maven迁移到mvnd。 参考资料&#xff1a;GitHub。 mvnd中会启动…

【数据结构篇】~二叉树(堆)

【数据结构篇】~二叉树&#xff08;堆&#xff09; 二叉树1.树2.树的组成3.二叉树4.堆1.向上调整算法2.向下调整算法3.堆排序 4.topk问题源码 二叉树 1.树 树的概念与结构​ 树是一种非线性的数据结构&#xff0c;它是由 n&#xff08;n>0&#xff09; 个有限结点组成一个…

BUG——GT911上电后中断一直触发

版型&#xff1a;正点原子 I.MX6UL MINI板 屏幕&#xff1a;7寸 1024*600 ATK-MD0700R V1.4 我的建议是买7寸屏幕就不要Mini板&#xff0c;因为Mini板太小装不下7寸屏幕&#xff0c;你需要一个更大的板子 简介&#xff1a; 算是作为一个后来者对这一现象的补充。解决方案就…

linux memory cgroup的memory.move_charge_at_immigrate含义

1.内核文档 上面的例子说明&#xff1a; 最开始某个进程是在cgroup A中&#xff0c;后面要迁移到cgroup B中&#xff0c;那么进程的内存计数是否要完全迁入B中&#xff0c;就是通过memory.move_charge_at_immigrate控制&#xff0c;如果目标cgroup也就是B设置了1到该字段中&am…

DBeaver安装使用

文章目录 简介支持的数据库支持的系统 下载安装DBeaver使用修改Maven下载jar地址窗口->首选项连接->驱动->Maven配置仓库地址 选择需要连接的数据库进行连接 简介 DBeaver 是一个通用的数据库管理工具和 SQL 客户端&#xff0c;支持 MySQL, PostgreSQL, Oracle, DB2,…

进存销系统

摘 要 伴随着我国全面推动信息化的趋势&#xff0c;我国的很多行业都在朝着互联网的方向进发。商品销售行业也有很多挑战。这次论文介绍的进存销系统就是为了能够解决当前传统商品进存销存在的问题&#xff0c;使得商品进存销能够更加有效率。电商智能化管理必不可少的帮手有进…

【VIsion Master】机器视觉软件二次开发(C#版本)学习笔记

0.前言 最近接手新项目&#xff0c;用海康威视旗下的HIK ROBOT Vision Master机器视觉软件做二次开发相关的项目&#xff0c;写一篇博客记录一下学习过程。 参考视频&#xff1a;https://www.bilibili.com/video/BV1tq4y1j7RP?p1 其他参考资料&#xff1a;软件自带的开发文档…

学习2d直线拟合-2

参考文章 直线拟合算法&#xff08;续&#xff1a;加权最小二乘&#xff09;_加权拟合直线法-CSDN博客 对比了参考文中和opencv中的直线拟合权重&#xff0c;不知道理解的对不对&#xff0c;前者是权重平方&#xff0c;后者没有平方 QtWidgetsApplication1::QtWidgetsApplic…

Excel中的“块”操作

在Excel中&#xff0c;有offset、index、indirect三个对“区域”操作的函数&#xff0c;是较高版本Excel中“块”操作的利器。 (笔记模板由python脚本于2024年08月20日 19:25:21创建&#xff0c;本篇笔记适合喜欢用Excel处理数据的coder翻阅) 【学习的细节是欢悦的历程】 Pytho…

幅频特性曲线分析及使用WPF绘制

文章目录 1、一阶惯性环节的幅频特性曲线分析及绘制2、二阶系统的幅频特性曲线分析及绘制3、一般的系统4、上位机代码实现4.1 一阶惯性系统4.2 二阶系统 5、稳定裕度5.1 幅值裕度5.2 相角裕度 参考 1、一阶惯性环节的幅频特性曲线分析及绘制 这里的a和b可以根据系统的不同修改,…

网络udp及ipc内存共享

大字符串找小字符串 调试 1. 信号处理函数注册&#xff1a;•一旦使用 signal 函数注册了信号处理函数&#xff0c;该函数就会一直有效&#xff0c;直到程序结束或者显式地取消注册。2. 注册多次的影响&#xff1a;•如果多次注册同一信号的处理函数&#xff0c;最后一次注册的…

【记录】基于Windows系统安装rust环境的过程

到官网下载安装包【入门 - Rust 程序设计语言 (rust-lang.org)】 ![[Pasted image 20240703142911.png]] 选择1&#xff0c;快速安装 选择编译配置&#xff0c;1为标准 安装完成 验证是否安装完毕 rustc --versioncargo --version验证成功&#xff01;

UneMeta创始人讲述自己在Web3+IP领域创业的心路历程

昨日&#xff0c;UneMeta创始人&#xff0c;Ann_tyrion在X分享了一篇推文&#xff0c;分享了自己在探索Web3与IP产业结合过程中的心路历程&#xff0c;她并没有像很多项目方那样一味的讲述宏大的叙事&#xff0c;而是字里行间透露出对这个行业的探索和不断给自己充实信念&#…