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内部的的一次函数调用,不依赖第三方途径。

相关推荐
大树889 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠9 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质9 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush49 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52010 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz10 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工10 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智11 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
不会C语言的男孩11 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
shushangyun_11 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化