理解Linux环境:从命令行参数到环境变量

1. 命令行参数

在有的地方会看到main函数的参数有下面这种写法:

C 复制代码
#include <stdio.h>

// agrc表示的是argv[]的元素个数,argv[]是指向一个一个字符串的指针数组(以NULL结尾)
int main(int argc, char* argv[]) 
{
    printf("hello world!\n");
    return 0;
}

那么argv[]到底指向谁呢?直接用代码来看一下:

C 复制代码
#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[0]:./a.out,那么再加上一个-a、-b、-c呢?

显而易见,在命令行输入的命令都变成字符串,存在了argv[]这个指针数组里,并且argv[0]永远是指向我们的可执行程序的名字。那么为什么这么做呢? 下面再来看一段代码:

C 复制代码
#include <stdio.h>

// 这段代码的主要功能是检查命令行参数的数量。如果参数数量不等于2,则输出使用说明并返回1。
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        printf("Usage: %s -v1/-v2/-v3\n", argv[0]);
        return 1;
    }

    return 0;
}

运行后就可以看到出现了提示:

把代码稍作修改一下:

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

// 这段代码首先检查命令行参数的数量是否为2,如果不是则输出使用说明并返回1。
//然后根据第二个参数(功能选项)执行相应的功能。如果输入的功能选项不支持,则输出"功能不支持"。
int main(int argc, char* argv[])
{
    // 检查参数数量是否正确
    if (argc != 2)
    {
        printf("Usage: %s -v1/-v2/-v3\n", argv[0]);
        return 1;
    }

    // 根据参数执行相应的功能
    if (strcmp(argv[1], "-v1") == 0)
    {
        printf("这是功能1\n");
    }
    else if (strcmp(argv[1], "-v2") == 0)
    {
        printf("这是功能2\n");
    }
    else if (strcmp(argv[1], "-v3") == 0)
    {
        printf("这是功能3\n");
    }
    else
    {
        printf("功能不支持\n");
    }

    return 0;
}

这时再运行:

这时就可以解答上面的问题了:为什么这么做? 在使用Linux 系统的时候,例如ls命令,后面可以跟着不同的选项,比如:ls -l -a,而命令的本质是C或者C++程序 ,选项本质上就是字符串 ,这就让选项可以使用一定的方式传递给程序内部的main函数,在程序实现的时候就能根据不同的选项,实现不同的功能。

那么这些命令行参数首先会被谁拿到呢?是被Bash拿到了,前面文章有提到过,我们所运行的程序的父进程都是Bash,并且调用fork函数后,只要父进程不做修改,父进程和子进程的代码和数据是共享的,所以子进程就可以拿到命令行参数。

具体是怎么做的后面文章再解释。

这时可以输出几个结论:

  1. 一个程序在运行的时候,argc最小为1,argv[]最少有一个元素(程序名)。
  2. 有几个子串(空格隔开),argc就是几。

2. 环境变量

环境变量是系统级别的全局变量,不同的变量具备不同的用途。

3. Linux环境变量

使用env命令可以查看当前系统的所有环境变量,下面是几个主要的环境变量解释:

  1. SHELL:当前用户使用的登录shell。
  2. HISTSIZE:命令历史记录的大小,下图是1000,表示最多保存1000条命令历史。
  3. LANGUAGE:系统语言环境。
  4. HISTTIMEFORMAT :命令历史记录的时间格式,这里是%F %T,表示记录日期和时间。
  5. PWD:当前工作目录的路径。
  6. LOGNAME:当前登录用户的用户名。
  7. HOME:用户的主目录路径,也就是家目录。
  8. USER:当前登录用户的用户名。
  9. PATH:可执行文件的搜索路径。
  10. OLDPWD:上一个工作目录的路径。

环境变量提供了关于当前用户会话、系统配置和环境设置的信息。

3.1 PATH

为什么在执行ls等命令的时候,不需要指定路径,而运行我们自己写的程序就需要指定路径,否则就会报错呢?是因为OS要找我们的执行程序,那他到哪里去找呢?是去/usr/bin这个路径下去找,因为PATH包含了这个路径。所以操作系统查找可执行命令,是在环境变量PATH中查找的。Windows配置环境变量也是这个目的,可以让程序直接使用命令行的方式运行,不需要带指定的路径。

使用echo $PATH命令来查看。在运行命令的时候,系统会在PATH中的多个路径寻找(不同路径使用 " : " 隔开)。

修改PATH:

  1. 使用PATH=''即会删除所有环境变量。
  2. export PATH=$PATH:要添加的路径即可添加新的环境变量。

4. 如何获取环境变量

4.1 main函数获取

在上面展示了main函数可以有两个参数,但实际上还可以有第三个参数:

C 复制代码
// 这里的env也是一个指针数组(以NULL结尾),指向了一个个字符串,也就是环境变量的K==V的字符串。
int main(int argc, char* argv[], char* env[]) {} 

接下来使用main函数获取一下环境变量:

C 复制代码
#include <stdio.h>
#include <string.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;
}

4.2 getenv函数获取

还有另一种获取环境变量的方法:getenv函数。

  • 功能:获取环境变量的值。
  • 用法:char *getenv(const char *name);
  • 参数:name 是环境变量的名称。
  • 返回值:如果找到指定的环境变量,则返回该变量的值;否则返回 NULL
C 复制代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main()
{
    char* path = getenv("PATH");
    printf("PATH = %s\n", path);
    return 0;
}

4.3 environ全局指针获取

C 复制代码
#include <stdio.h>
#include <stdlib.h>

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

5. 为什么要有环境变量

下面这段代码通过getenv函数获取环境变量USER,对比是否是指定用户运行程序。

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

int main()
{
    char* who = getenv("USER");
    if(strcmp(who, "djn"))
    {
        printf("非指定用户,无法执行!\n");
        return 1;
    }
    printf("用户正确,程序已执行!\n");

    return 0;
}

用户名正确:

即使是root用户也无法运行:

那么如果想写一个获取当前路径的代码,只需要获取环境变量中的PWD即可:

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

int main()
{
    char* pwd = getenv("PWD");
    printf("%s\n, pwd");
    return 0;
}

通过上面例子就可以了解:为什么要有环境变量?因为不同的环境变量有不同的用途。

相关推荐
脏脏a23 分钟前
【Linux篇】Linux指令进阶:从入门到熟练的实操指南
linux·运维·服务器
東雪蓮☆1 小时前
MySQL 5.7 主主复制 + Keepalived 高可用配置实例
linux·运维·mysql
迎風吹頭髮2 小时前
UNIX下C语言编程与实践20-UNIX 文件类型判断:stat 结构 st_mode 与文件类型宏的使用实战
linux·c语言·unix
凤凰战士芭比Q2 小时前
部署Nginx(Kylinv10sp3、Ubuntu2204、Rocky9.3)
linux·运维·nginx
讓丄帝愛伱2 小时前
Vim核心操作
linux·编辑器·vim
天上飞的粉红小猪2 小时前
进程的概念(下)
linux
NiKo_W2 小时前
Linux 自定义shell命令解释器
linux·bash·shell
七七七七072 小时前
【Linux 系统】命令行参数和环境变量
linux
jz_ddk2 小时前
[嵌入式] U-Boot 环境变量深度解析:从 QSPI 到 eMMC 的 Linux 启动完整指南
linux·运维·服务器·嵌入式·环境变量·u-boot·内核加载
CC.GG3 小时前
【Linux】Linux调试器----gdb/cgdb
linux