理解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;
}

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

相关推荐
db_murphy2 小时前
Oracle数据块8KB、OS默认认块管理4KB,是否需调整大小为一致?
linux
mCell5 小时前
从删库到跑路?这50个Linux命令能保你职业生涯
linux·windows·macos
杰克逊的日记5 小时前
GPU运维常见问题处理
linux·运维·gpu
誰能久伴不乏6 小时前
Linux系统调用概述与实现:深入浅出的解析
linux·运维·服务器
程序员学习随笔6 小时前
Linux进程深度解析(2):fork/exec写时拷贝性能优化与exit资源回收机制(进程创建和销毁)
linux·运维·服务器
mmoyula6 小时前
【RK3568 PWM 子系统(SG90)驱动开发详解】
android·linux·驱动开发
-SGlow-6 小时前
MySQL相关概念和易错知识点(2)(表结构的操作、数据类型、约束)
linux·运维·服务器·数据库·mysql
代码改变世界ctw7 小时前
Linux内核设计与实现 - 第14章 块I/O层
linux·运维·服务器
van叶~9 小时前
Linux网络-------1.socket编程基础---(TCP-socket)
linux·网络·tcp/ip
风吹落叶花飘荡9 小时前
Ubuntu系统 系统盘和数据盘扩容具体操作
linux·运维·ubuntu