目录
1. 命令行参数
C语言中main()函数其实是有参数。
#include <stdio.h>
int main(int argc, char *argv[])
{
return 0;
}
argv[] 是个字符指针数组,指向一个一个命令行参数字符串,数组中最后一个元素为NULL,表示到达结尾。argc 是上述 argv[] 数组元素的个数。
用以下代码演示 argc 和 argv[] 的存在和作用。
#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;
}
上述代码依次会打印出命令行中程序执行的命令和选项。这里的命令(./code)和选项(a, b, c)在下面统称为命令行参数。
命令行参数的用途是使程序能够通过不同的选项实现不同的子功能。
知识点1
main函数的命令行参数,是实现程序不同子功能的方法。Linux 系统中的指令也是用C语言写的,所以main函数的命令行参数也是指令选项的实现原理。
如下代码简单模拟指令的选项功能。
#include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s [-a|-b|-c]\n", argv[0]); return 1; } const char *arg = argv[1]; if (strcmp(arg, "-a") == 0) printf("这是功能1\n"); else if (strcmp(arg, "-b") == 0) printf("这是功能2\n"); else if (strcmp(arg, "-c") == 0) printf("这是功能3\n"); else printf("Usage: %s [-a|-b|-c]\n", argv[0]); return 0; }
知识点2
进程在运行的时候拥有一张argv表,用来存储所输入的每个命令行参数,实现选项功能。
2.环境变量的基本概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
例如在编写C/C++代码链接动静态库时,我们所链接的动静态库不知道在哪,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,在系统当中通常具有全局特性。
3. 查看环境变量方法
env //罗列出所以的环境变量
环境变量是以 名称=内容 的形式进行展现的。
echo $NAME //NAME:环境变量名称
4. 常见的环境变量
4.1 PATH
PATH:系统中搜索命令的默认搜索路径。
下面在当前工作目录(/home/csq/linux-learning-notes-and-code1/lesson15)下创建一个hello.c文件,编译为hello二进制文件:
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
查看当前的PATH环境变量,发现当前工作目录没有在环境变量PATH中,所以只能使用./hello来执行程序。使用 hello 直接程序,bash找不到文件
使用下列命令在PATH环境变量中添加当前工作目录
export PATH=$PATH:/home/csq/linux-learning-notes-and-code1/lesson15
这样就能在当前工作目录中直接运行 hello 来执行程序了。
如下还有其他的方法不带路径直接就可以运行:
1. 将自己写的二进制程序拷贝到环境变量PATH中存在的目录下,但是不推荐这样,因为PATH环境变量中默认存在的目录都是与系统配置相关的目录,如果将自己的程序拷贝到这些目录中,可能会污染系统指令的环境等。
2. 将当前的工作目录信息拷贝到系统相关配置文件中,使系统一启动就将该目录加载到环境变量PATH中。在CentOS7系统中,家目录下有两个隐藏文件 .bashrc 和 .bash_profile,修改.bash_profile中的环境变量PATH路径即可在启动程序时自动加载修改后的环境变量PATH。
知识点1:
环境变量是内存级的环境变量,每次启动一个bash,在bash进程中向内存申请一段空间来存储环境变量,每次重新启动系统时,之前修改过的环境变量都会重置,默认的环境变量最开始是系统相关配置文件中读取的。
知识点2:
bash进程启动时,会从系统中读取环境变量,形成一张环境变量表。也是一个字符指针数组,每个元素指向一个key - value 的环境变量长字符串。env查询的时候就是打印这张表中元素指向的内容。
每个程序都会收到⼀张环境表,环境表是⼀个字符指针数组,每个指针指向⼀个以’\0’结尾的环境字符串
知识点3:
执行 "ls -a -l "命令时,首先bash先拿到这个字符串,然后通过分割字符串,形成命令行参数表,然后去PATH环境变量的路径中查找 " ls " 命令,如果存在,就拼接命令路径创建子进程执行该命令。
4.2 HOME
HOME:指定用户的主工作目录(即用户登录到Linux系统中时默认的目录)。
echo $HOME //查看环境变量HOME
当执行 cd ~ 为什么会直接转到 /home/csq 中,因为在执行这条命令时,把 ~ 替换为环境变量 /HOME 中存储的目录,所以直接就跳转到 /home/csq 中了。
切换为 root 用户,环境变量HOME则会变为 /root.
4.3 其他环境变量
SHELL:当前Shell,代表的是使用哪个版本的Shell,它的值通常是/bin/bash这个目录。
USER:当前用户是谁。
LOGNAME:当前登录的用户是谁。
HISTSIZE:当前用户存储历史命令的最大的数量。
HOSTNAME:表示当前主机的主机名。
PWD:表示当前的工作目录。
OLDPWD:表示切换前的工作目录。cd - 命令中的 - 就是 OLDPWD对应的值。
知识点1:
su 命令相当于对当前用户提权,并不会更新环境变量中的USER和LOGNAME。su - 命令相当于使用root账号重新登录,会将USER和LOGNAME更新为root。
5. 环境变量相关的命令
echo $xxx //xxx表示环境变量名,查看指定的环境变量内容
env //查看当前用户的所有环境变量
export xxx=*** //给当前用户增加一个环境变量,环境变量名为xxx,环境变量内容为***
unset xxx //xxx表示环境变量名,表示删除一个环境变量
6. 通过代码如何获取环境变量
1. 通过命令行参数进行获得。
第一章讲命令行参数时,其实main函数的参数最多可以有三个,如下:
#include <stdio.h>
int main(int agrc, char *argv[], char *env[])
{
return 0;
}
用下列程序遍历父进程(bash)的环境变量表:
#include <stdio.h>
#include <string.h>
//main函数有参数吗?有
//最有可以有几个?3个
//该参数是父进程传递给我们的
int main(int argc, char *argv[], char *env[])
{
(void)argc;
(void)argv;
for (int i = 0; env[i]; i++)
{
printf("env[%d]-> %s\n", i, env[i]);
}
return 0;
}
知识点1:
main函数只是我们当前编写的代码的入口,不是程序启动的入口,当程序启动的时候会先调用一个start函数,检查main函数中参数的个数,然后把参数传给main函数,然后调用main函数执行我们自己的代码。下面是上述代码汇编语言中_start函数中调用main函数:
知识点2:
环境变量会被子进程继承,所以环境变量在系统中具有全局特性。但是如果任意一个子进程想修改环境变量,就和之前进程中的数据一样,会发生写时拷贝。
2. 通过getenv()函数获取环境变量
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *env[])
{
(void)argc;
(void)argv;
(void)env;
char *value = getenv("PATH");
if (value == NULL)
return 1;
printf("PATH->%s\n", value);
return 0;
}
写一个程序,只能让自己一个人用,连root都不能执行。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *env[])
{
(void)argc;
(void)argv;
(void)env;
const char *who = getenv("USER");
if (who == NULL) return 1;
if (strcmp(who, "csq") == 0)
{
printf("这是程序的正常执行逻辑\n");
}
else
{
printf("Only csq!!!\n");
}
return 0;
}
当使用 csq 用户执行时:
当使用 root 用户执行时:
知识点1:
为什么子进程要继承环境变量?
因为继承环境变量之后,子进程就可以结合环境变量来定制个性化操作,比如上述的编写一个只能用于一个人用的程序。
3. 使用全局变量char **environ来获取环境变量
该全局变量指针,指向环境变量表的第一个元素(第一个环境变量的地址),所以是一个二级指针。
用下列程序进行演示:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char *argv[], char *env[])
{
(void)argc;
(void)argv;
(void)env;
for (int i = 0; environ[i]; i++)
{
printf("env[%d]-> %s\n", i, environ[i]);
}
return 0;
}
7. 环境变量的特性和本地变量
bash会记录两套变量:1.环境变量 2.本地变量:不会被子进程继承,只在bash内部使用。
环境变量:具有全局特性。
本地变量:在shell中可以使用 i=10 直接定义本地变量,使用 echo $i 可以进行查询, unset i 取消变量 i 。
export i //i为本地变量名,可以将本地变量i导入到环境变量中
知识点1:
命令行输入的命令都是bash的子进程,因为进程之间具有独立性,export导入环境变量按道理是不能导入到父进程bash中的,但是为什么可以使用export将环境变量导入到父进程bash中呢?因为export是一种内建命令(built-in command),这种命令不需要创建子进程,而是让bash自己亲自执行。