Linux下 进程(五)(命令⾏参数和环境变量)

欢迎来到我的频道 【点击跳转专栏】

码云链接 【点此转跳】

文章目录

  • [1. 命令行参数](#1. 命令行参数)
    • [1.1 是什么](#1.1 是什么)
    • [1.2 为什么要有](#1.2 为什么要有)
    • [1.3 有关命令行参数的3个细节](#1.3 有关命令行参数的3个细节)
  • 2.环境变量
    • [2.1 环境变量的基本概念](#2.1 环境变量的基本概念)
    • [2.2 常⻅环境变量](#2.2 常⻅环境变量)
      • [2.2.1 PATH&&查看和修改环境变量⽅法](#2.2.1 PATH&&查看和修改环境变量⽅法)
      • [2.2.2 env(查看所有环境变量)](#2.2.2 env(查看所有环境变量))
      • [2.2.3 浅谈上述打印的环境变量](#2.2.3 浅谈上述打印的环境变量)
    • [2.3 通过代码如何获取环境变量](#2.3 通过代码如何获取环境变量)
      • [2.3.1 命令⾏第三个参数](#2.3.1 命令⾏第三个参数)
      • [2.3.2 通过第三⽅变量environ获取](#2.3.2 通过第三⽅变量environ获取)
      • [2.3.3 getenv(最常用的方法)](#2.3.3 getenv(最常用的方法))
    • [2.4 环境变量的全局属性](#2.4 环境变量的全局属性)
      • [2.4.1 bash进程的环境变量怎么来的](#2.4.1 bash进程的环境变量怎么来的)
      • [2.4.2 export(创建环境变量)](#2.4.2 export(创建环境变量))
      • [2.4.3 unset(删除环境变量)](#2.4.3 unset(删除环境变量))
      • [2.4.4 set(查看环境和局部变量)](#2.4.4 set(查看环境和局部变量))
      • [2.4.5 echo怎么打印本地变量的?](#2.4.5 echo怎么打印本地变量的?)

1. 命令行参数

1.1 是什么

main函数能不能有参数?

答案是有的

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    printf("argc: %d\n", argc);
    int i = 0;
    for(; i< argc ; i++)
    {
        printf("argv[%d]->%s\n", i, argv[i]);
    }
    return 0;
 }  
部分 说明
argc Argument Count:命令行参数的个数(包括程序名本身)
argv Argument Vector:一个字符串数组,每个元素是一个命令行参数

运行结果:


它的本质,就是提取你输入的字符串(数字会自动转换成char) )然后根据空格断开 自动提取对应的内容 存入到char* argv[]数组中:

⚠️:不过需要注意的是,我们是看不到这个过程的,这个是通过bash和系统自动完成的!

1.2 为什么要有

看个例子就明白了:

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("该命令使用错误! 你应该这样用: %s -a|-b|-c|-d\n", argv[0]);
        return 1;
    }

    if(strcmp(argv[1], "-a") == 0)
    {
        printf("我现在执行的是该命令的第一种功能\n");
    }
    else if(strcmp(argv[1], "-b") == 0)
    {
        printf("我现在执行的是该命令的第二种功能\n");
    }
    else if(strcmp(argv[1], "-c") == 0)
    {
        printf("我现在执行的是该命令的第3种功能\n");
    }
    else
    {
        printf("我现在执行的是该命令的默认功能\n");
    }
    return 0:
}
    

运行结果:

是不是跟ls -als -lls -n等等特别像!

💡:命令行参数本质应用,是为了实现一个命令,可以根据不同的选项,实现不同的子功能!也是Linux中所有命令选项功能的实现方式!!!

1.3 有关命令行参数的3个细节

  1. 细节1:命令行参数,至少是1argc >=1argv[0]一定会有元素,指向的就是程序名
  2. 细节2:选项,是以空格 分隔的字符串,一个字符,也是字符串!
  3. 细节3:参数有argc个,argv[argc-1]是最后一个字符串 ,而argv[argc] == NULL

2.环境变量

2.1 环境变量的基本概念

环境变量 (environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,属于系统级别的变量 ,变量名和变量内容,往往具有全局性

  • 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
  • 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

2.2 常⻅环境变量

2.2.1 PATH&&查看和修改环境变量⽅法

在我们日常输入命令的时候,只需要像ls一样直接输入命令就行了,而我们自己写的程序却必须要加上./这是为什么呢?

系统的命令大多都是C语言写的可执行文件,并且放在了usr/bin路径下,不要带./即可跑,这是因为它在usr/bin路径,是系统默认会自动匹配寻找的路径;如果我们把我们自己写的code文件,拷贝到usr/bin路径下,我们发现我们也能直接不带./就能运行!

这是因为我们系统执行程序前,需要将程序加载到内存中,在加载前,系统得找到程序在哪里,不过系统不会在当前目录下找,而是在特定路径 下找!./本质是告诉系统你的程序在哪里!`


Linux系统怎么知道在哪个路径寻找?谁告诉你的?还会不会去别的路径找呢?
Linux存在一个叫环境变量的东西是一个在全系统中全局有效的变量,属于shell语法自己定义的变量!而这里起作用的变量就叫:PATH

  • PATH : 指定命令的搜索路径,就是告诉吸引,如果用户执行一个可执行文件,没有指明路径,就会去PATH所指向的路径一个个找!

PATH是其的变量名,我们可以用echo $环境变量名称查看内容(以作为分隔符):


我们可以用 环境变量名=XXX的方式直接覆盖环境变量就行了

我们发现code可以直接运行,而ls无法运行了 这里直接把终端重启就恢复了


我么可以用环境变量名=$环境变量名:XXX的方式增加新的内容,这样原本的命令(ls等)也不会受到影响了

2.2.2 env(查看所有环境变量)

shell 复制代码
env : 显⽰所有环境变量

下面会展示显示内容并对变量添加介绍注释!

shell 复制代码
fcy@VM-16-15-ubuntu:~$ env

# 当前用户使用的 shell 程序
SHELL=/bin/bash

# 历史命令保存的最大条数(此处为 1000 条)
HISTSIZE=1000

# 历史命令时间戳格式:%F 表示 YYYY-MM-DD,%T 表示 HH:MM:SS
HISTTIMEFORMAT=%F %T 

# 当前工作目录(即用户执行 env 命令时所在的路径)
PWD=/home/fcy

# 当前登录用户名(等同于 whoami 的输出)
LOGNAME=fcy

# 当前会话类型(tty 表示通过终端/SSH 登录,而非图形界面)
XDG_SESSION_TYPE=tty

# 表示系统在登录时已显示"每日提示"(Message of the Day, MOTD)
MOTD_SHOWN=pam

# 用户主目录路径
HOME=/home/fcy

# 系统语言和字符编码(美式英语 + UTF-8)
LANG=en_US.UTF-8

# ls 命令中不同文件类型的显示颜色配置(例如目录蓝色、可执行文件绿色等)
LS_COLORS=rs=0:di=01;34:ln=01;36:...(省略部分为各类文件扩展名的颜色规则)

# 每次命令执行后自动追加到历史文件(确保多终端间历史同步)
PROMPT_COMMAND=history -a; history -a; 

# SSH 连接信息:客户端IP 客户端端口 服务器IP 服务器端口
SSH_CONNECTION=49.80.114.131 29107 10.0.16.15 22

# less 命令关闭时调用的脚本(用于清理临时文件等)
LESSCLOSE=/usr/bin/lesspipe %s %s

# 会话类别(普通用户会话)
XDG_SESSION_CLASS=user

# 终端类型(支持 256 色的 xterm 兼容终端)
TERM=xterm-256color

# less 命令打开文件前调用的预处理器(如解压 .gz 文件再查看)
LESSOPEN=| /usr/bin/lesspipe %s

# 当前用户名(与 LOGNAME 相同)
USER=fcy

# Shell 嵌套层级(1 表示这是最外层 shell)
SHLVL=1

# 当前会话的唯一 ID(由 systemd-logind 分配)
XDG_SESSION_ID=301948

# 用户运行时目录(存放 socket、pid 等临时运行文件)
XDG_RUNTIME_DIR=/run/user/1006

# SSH 客户端连接信息:IP 地址、源端口、目标端口
SSH_CLIENT=49.80.114.131 29107 22

# 应用程序数据目录搜索路径(用于查找图标、桌面文件等)
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop

# 系统可执行程序的搜索路径(最后多了自定义路径)
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/fcy/linux_code/lesson18

# D-Bus 会话总线地址(用于进程间通信)
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1006/bus

# 当前 SSH 会话绑定的伪终端设备
SSH_TTY=/dev/pts/0

# 最后执行的命令路径(这里显示的是 /usr/bin/env 本身)
_=/usr/bin/env

2.2.3 浅谈上述打印的环境变量

我们可以用history查看历史命令 而其记录的上线就是通过HISTSIZE这个环境变量控制的!

  • HISTSIZE:历史命令保存的最大条数。

而当我们whoamipwd的时候 本质也是打印对应的环境变量 USERPWD

  • USER:当前用户名
  • PWD:当前工作目录

其中SHELL=/bin/bash,我们cd ~为什么能回到家目录 也是因为HOME 记录了我么的家目录位置。

  • SHELL : 当前Shell,它的值通常是/bin/bash(也就是具体的shell是什么)
  • HOME : 指定⽤⼾的主⼯作⽬录

⚠️:其实还有很多比如 cd -等等 但原理大差不差就不写了!

2.3 通过代码如何获取环境变量

方法 可移植性 是否需要 main 参数 说明
char *env[] in main ❌(仅 POSIX/GCC) 直接遍历所有环境变量
extern char environ ❌(POSIX) 全局变量,可在任意函数使用
getenv("KEY") ✅(C 标准) 只能查单个变量,最安全

2.3.1 命令⾏第三个参数

其实在main中还存在第三个命令行参数char* env[]

参数 含义
argc 命令行参数个数(argument count)
argv 命令行参数数组(argument vector),argv[0] 是程序名
env 环境变量表(environment vector),以 NULL 结尾
cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char *argv[],char *env[])
{
        int i = 0;
        for(;env[i];i++)
        {
                printf("env[%d]:%s\n",i,env[i]);
        }

        printf("这是我自己实现的命令\n");
        return 0;
}

运行结果:

本质是把环境变量表 传递给了进程 ,每个程序都会收到⼀张环境表 ,环境表是⼀个字符指针数组 ,每个指针指向⼀个以'\0'结尾的环境字符串:

2.3.2 通过第三⽅变量environ获取

在C\C++标准库中,libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时 要⽤extern声明。
extern:在 C 和 C++ 中,extern 是一个存储类说明符,用于声明变量或函数是在其他编译单元中定义的,当前文件只是"引用"它,而不是"定义"它,通俗讲就是告诉编译器:"这个符号在别处定义,链接时请去别处找。常用于声明外部全局变量"

关键字 链接属性 作用
extern 外部链接 全局可见,可在其他文件使用
static(全局变量/函数) 内部链接 仅本文件可见,隐藏实现
cpp 复制代码
static int secret = 100;   // 其他文件无法访问,除非包头文件
extern int shared = 200;   // 其他文件可通过 extern 声明使用,不用包头文件!
cpp 复制代码
int main()
{
extern char **environ;
    int i = 0;
    for(; environ[i]; i++)
    {
        printf("environ[%d]->%s\n", i, environ[i]);
    }
    return 0;
}

我们发现也能正常打印:

2.3.3 getenv(最常用的方法)

cpp 复制代码
#include <stdlib.h>
char *getenv(const char *name);

原理就是遍历环境变量表 获取对应的环境变量getenv常来访问特定的环境变量。

cpp 复制代码
int main()
{
   char *whoami = getenv("USER");
   printf("whoami: %s\n", whoami);
   if(whoami == NULL)
   {
       printf("无法执行我,因为我不认识你!\n");
       return 0;
   }
   else if(strcmp(whoami, "root") == 0)
   {
       printf("我也不准root执行!\n");
   }
   else if(strcmp(whoami, "fcy") == 0)
   {
       printf("你是合法的用户,我让你执行\n");
   }
 }  

fcy账户执行:

root执行:

2.4 环境变量的全局属性

我们所知的argv和env都是默认在bash内部的,而bash本身是个进程,所以这两张表本质是在内存中的两张临时的表!而众所周知 我的程序命令等,都是bash进程的子进程 !而子进程 是可以共享父进程 的代码和数据的(如果子进程 不改数据的话)。

所以我们可以这么理解:全局变量表 本质就是bash进程的数据!当我们运行自己的程序的时候,bash就会通过fork创建子进程,而环境变量 具有全局属性,此时

  • 环境变量 不是某个函数或类的局部数据,而是属于整个进程;
  • 无论你在 main()、某个工具函数、还是第三方库中调用 getenv("PATH"),都能拿到相同的值。

是可以直接被⼦进程继承 下去(在未修改的情况下)!这就是哪怕我们不传char* env[]参数依然可以通过另外两个方法拿到全局变量的参数的原因!

2.4.1 bash进程的环境变量怎么来的

那么bash进程的环境变量又是从哪里来的呢?

是从Linux的系统配置文件下获得的。


那么配置文件是从哪里获取的?

一般在家目录的.bashrc中获取,然后通过.bashrc读取系统的配置文件(一般在/etc路径下)

2.4.2 export(创建环境变量)

  • export:可以将本地变量转换为全局变量,本质是把局部变量写到环境变量表里,属于内存级操作 ,当再次打开bash会重置!若想要永久存在,则需要修改系统的配置文件
  • 本地变量:无法被子进程继承,不具备全局性,只能在bash内部被访问的变量。

2.4.3 unset(删除环境变量)

unset: 清除环境变量,将对应环境变量从表中删除,本质还是内存级,不影响配置文件。

2.4.4 set(查看环境和局部变量)

set: 显⽰本地定义的shell变量和环境变量。

2.4.5 echo怎么打印本地变量的?

  • 按照上面的说法 我们的echo应该也是bash的一个子进程,而子进程按道理来说是没办法继承局部变量的!但下图的echo依然可以打印局部变量。
  • 我们还发现在修改PATH的情况下,有些命令还依然能跑,这是为什么呢??

⚠️:这里可以得出个结论(目前了解这么多就行):命令和命令是不一样的!存在二进制文件级别的普通命令!是磁盘级,会加载成子进程然后执行同时也存在内建命令:在shell内部自己定义,不创建子进程,只是bash内部的的一次函数调用,不依赖第三方途径。

相关推荐
安科士andxe7 小时前
深入解析|安科士1.25G CWDM SFP光模块核心技术,破解中长距离传输痛点
服务器·网络·5g
小白同学_C10 小时前
Lab4-Lab: traps && MIT6.1810操作系统工程【持续更新】 _
linux·c/c++·操作系统os
今天只学一颗糖10 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
2601_9491465310 小时前
Shell语音通知接口使用指南:运维自动化中的语音告警集成方案
运维·自动化
儒雅的晴天11 小时前
大模型幻觉问题
运维·服务器
Gofarlic_OMS12 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
通信大师12 小时前
深度解析PCC策略计费控制:核心网产品与应用价值
运维·服务器·网络·5g
dixiuapp12 小时前
智能工单系统如何选,实现自动化与预测性维护
运维·自动化
不做无法实现的梦~12 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
Elastic 中国社区官方博客12 小时前
如何防御你的 RAG 系统免受上下文投毒攻击
大数据·运维·人工智能·elasticsearch·搜索引擎·ai·全文检索