命令行参数&环境变量
命令行参数
main函数也是一个函数,其实也可以携带参数的
int main( int argc, char *argv[ ], char *envp[ ] )
{program-statements
}
那这里是有三个参数的:
第一个参数: argc 是个整型变量,表示命令行参数的个数(含第一个参数)。
第二个参数: argv 是个字符指针的数组,每个元素是一个字符指针,指向一个字符串。这些字符串就是命令行中的每一个参数(字符串)。
第三个参数: envp 是字符指针的数组,数组的每一个原元素是一个指向一个环境变量(字符串)的字符指针。
先来说前两个参数:
1 #include <stdio.h>2 int main(int argc,char* argv[])3 {4 int i = 0;5 for(;i<argc;i++)6 {7 printf("%d:%s\n",i,argv[i]); 8 }9 10 return 0;11 }
它直接把我们上一步输入的命令行给打印出来了,说明我们上一步的命令行被main函数给保存到了argv数组中去,那么,我们尝试着在命令行中在写多一些字符串,来证实一下我们的猜想:
是这样的
用命令行参数就可以实现一个简单的交互程序:
1 #include<stdio.h>2 #include<string.h>3 #include<stdlib.h>4 5 int main(int argc,char* argv[])6 {7 if(argc != 4)8 {9 printf("Use error\nUsage: %s op[-add|sub|mul|div] d1 d2\n", argv[0]); //argv[0], 会不会不存在呢??10 return 1;11 } 12 int x = atoi(argv[2]); 13 int y = atoi(argv[3]); 14 int result = 0;15 // 你的程序一定有4个命令行参数,第一个是程序名16 if(strcmp(argv[1], "-add") == 0) 17 {18 result = x + y; 19 printf("%d+%d=%d\n", x, y, result);20 } 21 else if(strcmp(argv[1], "-sub") == 0) 22 {23 result = x - y;24 printf("%d-%d=%d\n", x, y, result);25 } 26 else if(strcmp(argv[1], "-mul") == 0) 27 {28 result = x * y; 29 printf("%d*%d=%d\n", x, y, result);30 } 31 else if(strcmp(argv[1], "-div") == 0) 32 {33 if( 0 == y ) printf("%d/%d=error! div zero\n", x, y);34 else printf("%d/%d=%d\n", x, y, x/y);35 } 36 else 37 {38 printf("Use error, you should use right command line\nUsage: %s op[-add|sub|mul|div] d1 d2\n", argv[0]); / /argv[0], 会不会不存在呢??39 }40 41 return 0;42 }
还可以模拟实现Linux中的bash指令
当模拟实现了计算器后,我们可以发现,其实Linux下的指令如ls,pwd或touch,其实就是调用了不同的可执行程序来完成的! 我们便不难理解我们的命令行选项的概念了。
在Linux中,命令行选项是通过命令行输入的参数,用于修改命令的行为或提供额外的功能。命令行选项通常以短划线(-)或双短划线(--)开头,后面跟着一个字母或单词。这些选项允许用户在执行命令时自定义命令的行为,通过不同的选项组合可以实现各种不同的功能。我们经常使用的指令其实就相当于一个个可执行程序,后面的一些像 -l,-a,-al之类的均为命令行参数,相当于可以执行不同的功能 。
比如touch指令,输入touch和要创建的文件名本质就是在C语言程序中创建一个和输入的文件名一样的文件来间接创建文件
#include<stdio.h>
int main(int argc,char* argv[])
{ if(argc != 2)//输入的字符串不规范 { printf("touch missing file operand\n"); return 1; } FILE* fp = fopen(argv[1],"w"); if(fp!=NULL) fclose(fp); return 0;
}
环境变量
我们在使用一些集成开发环境时,需要我们自己配置环境变量用来满足代码编译需求.
基本概念
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
在Linux中运行代码时我们就会发现,同样是可执行程序,我们自己写的代码就需要带 ./ 才能运行,但是系统中的命令,比如whami 、ls等却不用带 ./ 照样可以执行呢?其实,这里的 ./表示了我们的当前目录,带着目录运行一个可执行程序当然是可以的,我们知道,要执行一个可执行,必须找到自己的可执行在哪里,但是,为什么系统的指令就可以不带目录而独自运行呢?所以:系统指令有自己的默认的查找路径!
查看系统的环境变量
我们可以使用命令:
echo $PATH
在查询结果中,每个环境变量之间以 : 分隔开, 从上面的结果中我们可以看到, 一共有6个系统的默认的环境变量, 我们在使用系统命令时, 系统会自动挨着在这些目录下查找是否有对应的可执行文件, 有就执行, 没有就报错。
添加环境变量
知道了这个功能,我们想尝试着将我们自己的可执行文件的目录添加到环境变量中去,这样,我们运行我们的程序就不用加目录了,我们直接来试一下:
使用指令:
PATH = $PATH:要添加的路径
$PATH表示当前的环境变量, $PATH:[路径]表示将路径追加到原来的PATH中, 如果直接 PATH = [路径], 原来的内容会被覆盖, 和给变量赋值是一样的
这里需要注意,默认的环境变量的修改,仅限于当前的这一次登录,如果用户退出再登录,环境变量就会恢复到原来的状态。这个我们在这里先简单的介绍一下原因,具体后面还会提及:
我们知道,命令行启动的进程都是bash(命令行解释器)的子进程,子进程的命令行参数和环境变量,是父进程传递给的 ,我们每次更改子进程的环境变量信息,实际上更改的是bash进程内部的环境变量信息,而每一次的重新登录,我们知道,bash也是一个进程,所以,我们每次登录时,系统就会为我们创建一个新的bash解释器,重新读取环境变量信息保存到自己的数据信息中。
那么,问题又来了,bash又是从哪里读取到环境变量的信息的呢?
其实这个环境变量信息是以配置文件的方式存在的,也就是脚本形式存在的,这个配置文件在对应用户的家目录的一个隐藏文件 .bash_profiles 中,每次用户登录,都会读取该文件中的内容,该文件会为bash进程生成环境变量表信息。
当我们在配置文件中加上自己想要一直存在的环境变量时,此时bash再次启动后,这个环境变量就会被放到环境变量表中!
并且在创建子进程时,这张表会通过main函数的参数传递给子进程,所以子进程有了和父进程一样的环境变量表,而子进程创建它的子进程时又会将子进程的表给孙子进程, 所以说,环境变量具有全局性!
在bash中查看所有环境变量
在bash中查看所有环境变量:
使用指令:
env
环境变量PWD:
记录当前路径,你以为平时执行的pwd指令是怎么知道当前路径的?pwd实际上调用了PWD环境变量!
环境变量HOME
我们平时使用的指令:cd ~进入家目录,环境变量HOME里面的内容就是家目录!
根据以上对环境变量的认识,我们发现登录xshell时我们账户登录后就会到 /root 或者 /home/用户名 目录下,这是因为在启动Linux时,OS帮我们做了一些事情
1.输入用户名,密码
2.认证信息是否正确
3.形成环境变量(PATH,PWD,等等)
4.根据用户名初始化HOME环境变量
6.进入家目录
程序中获取环境变量的方式
使用函数:
getenv()
printf("PATH = %s\n",getenv("PATH"));
即函数的参数是环境变量的名字, 返回值是环境变量的内容!
main函数的第三个参数解析
int main(int argc,char* argv[],char* env[])
第三个参数为env,是不是很熟悉?
第三个参数就是环境变量,它和第二个参数一样是指针数组,它指向环境变量表,环境变量表中的内容和在bash中使用env打印的一样!
到目前为止了解到,系统在启动程序时会给main函数提供两张表:
命令行参数表和 环境变量表
通过下面的代码查看环境变量表:
1 #include <stdio.h>
2 #include <stdlib.h>
3 int main(int argc,char* argv[],char* env[])
4 {
5
6 int i = 0;
7 for(;env[i];i++)
8 {
9 printf("env[%d] = %s\n",i,env[i]);
10 }
11 return 0;
12 }
本地变量&环境变量
我们可以在bash中直接定义环境变量
使用指令:
环境变量名=内容
用户自己定义的环境变量是本地变量的而env并不能查看本地的环境变量,
所以使用查找刚才定义的环境变量
时,实际上会找不到!
如果想要我们定义的环境变量被放在系统的环境变量表中应该怎么做?
使用指令:
export 环境变量名
还可以使用export直接定义环境变量:
本地变量的一般形式为: [名称] = [内容] ,其只能在当前bash进程的内部有效,也就是说,由bash创建的子进程就不能再使用这个本地变量,下面是利用fork函数的证明:
我们发现,一开始子进程并没有找到对应的本地变量的内容, export将变量导入环境变量表环境变量就可以供子进程使用了.
本地变量 VS 环境变量
1. 本地变量只在bash进程内部有效, 不会被子进程继承
2. 环境变量通过让所有子进程继承的方式, 实现自身的全局性
内建命令与常规命令
我们发现像一些系统定义的命令,比如echo这种命令,却都能够获取本地变量的内容,众所周知,这些命令本质上也是bash所创建的一个子进程,按理来说,它们也应该获取不到本地变量的值才对,这是为什么呢?
在Linux中,命令分为常规命令(shell fork让子进程创建形成的)和内建命令(shell命令行的一个函数), 而这些简单而又安全的命令, 比如pwd,echo和export之类的命令可以作为shell的函数而在不创建子进程的同时读取shell本地的变量。echo是内建命令,是bash父进程自己内部执行的,所以它可以使用父进程的本地变量, 但是其他的子进程不能接受父进程的本地变量,所以无法读取!
我们知道一个事实,bash中的指令可以直接使用, 不用加./是因为它的路径在环境变量PATH中,所以我们
将PATH置空后,这些常规命令就不能运行了!因为bash无法
找到可执行程序的目录,如果我们想要运行一个可执行程序,需要告诉系统我们的可执行的目录,让系统自动找到可执行的目录得以执行程序。echo等命令是内建命令,是shell命令函的一个函数, 可以直接执行还能读取shell内部定义的本地变量.
此时PATH设置为了空,上面说过可以重新登陆新建一个bash进程重新读取PATH变量, 这个时候, 我们再来测试环境变量myvarible还存不存在,会不会被重置
我们发现,环境变量又没了,这其实和我们上面添加环境变量处的情况相似,我们的bash解释器是将环境变量拷贝到了内存,而bash进程是在内存中运行的,我们对内存的所有操作,都会因为当前bash进程的退出(用户退出)而被销毁, 如果想一直保留新建的环境变量该怎么办呢?
从上面的原理中,我们知道了在用户登录的时候 .bash_profile 这个配置文件会识别用户属性然后为用户配置环境变量表和命令行参数表,那么,我们可以将我们的环境变量信息添加到这个配置文件中:
当我们再次重新登录用户,我们发现此时环境变量已经被保存了下来,并且能被我们查询到。
当然,因为我们的配置文件是因用户而异的,也就是说,每一个用户的配置文件都不一定相同,所以,我们当前的配置,在其他用户下不能生效(包括root用户)。但是,对于同一个用户,只要我们修改了其配置文件,bash所有的子进程、孙子进程等进程都可以使用使用该环境变量了。
所以,现在我们应该能大题了解了什么是环境变量,环境变量本质就是找到可执行程序的目录,如果我们想要运行一个可执行,需要告诉系统我们的可执行的目录,好让系统自动找到可执行的目录得以执行程序。
当然,因为我们的配置文件是因用户而异的,也就是说,每一个用户的配置文件都不一定相同,所以,我们当前的配置,在其他用户下不能生效(包括root用户)。但是,对于同一个用户,只要我们修改了其配置文件,bash所有的子进程、孙子进程等进程都可以使用使用该环境变量了。
通过第三方变量environ获取
如果不给main函数穿第三个参数,通过environ变量也是可以访问环境变量的:
在Linux中,environ是一个全局变量,它是一个字符串数组,用于存储当前进程的环境变量。它是一个二级指针,每个指针指向一个以"key=value"形式表示的环境变量字符串。
environ变量被定义在C标准库的<unistd.h>头文件中,并由操作系统在程序启动时自动创建和初始化。它存储了操作系统为当前进程提供的环境变量的副本。通过environ变量,可以访问和操作当前进程的环境变量。例如,可以使用循环遍历environ数组,查看和修改环境变量的值。
#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(){extern char** environ;int i = 0;for(;environ[i];i++){printf("我是进程%d,%s\n",getpid(),environ[i]); }return 0;
}
总结:目前有三种访问环境变量的方式:
1.echo $环境变量
2.通过main函数参数列表的第三个参数环境变量表来访问
3.通过environ来访问
一些其他关于环境变量的命令
除了上面的export、env、echo等命令,我们再来介绍两个命令:
unset: 清除环境变量
set: 显示本地定义的shell变量和环境变量