这篇博客很大程度上借鉴了三位大佬的博客,可以说是拾人牙慧,感谢三位大佬的分享,三篇博客的网址分别是
https://blog.csdn.net/u014530704/article/details/73848573
https://www.cnblogs.com/leijiangtao/p/4051387.html
https://blog.csdn.net/libinbin_1014/article/details/51490568
一、execl函数的使用
int execl(const char *path, const char *arg, ...);
返回值:
execl函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
当进程调用exec族函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
1.excel功能验证
首先我们先在当前路径下建立一个echoarg.c文件,代码如下
接着编译运行,创造一个可运行的echoarg文件,结果如下
接着我们利用excel编写代码,看一下该函数的功能
这里要说一下,当你使用excel函数的时候,第一个参数是文件路径,无论你是相对路径还是绝对路径一定要保证路径正确,否则是没办法成功使用excel函数的。同时还要注意,无论你的arg参数有多少个,最后一个一定是NULL,否则也无法成功调用。
运行结果如下,我们可以看到,当excel函数使用成功的时候,它就调用了echoarg文件,并且直接运行该文件的程序了,他原来的程序就不管了。
下面我们再来举出一些失败的例子
案例一:路径不对,代码如下图所示(我的echoarg在当前路径,不是在bin路径下)
运行结果如下,可以看出,execl函数调用失败后返回-1。并且perror函数已经告诉了我们失败的原因,是路径的错误。
案例二:arg参数最后一个不是NULL ,代码如下
运行结果如下,可以看到,如果arg参数最后一个不是NULL,编译都无法通过
2.使用excel函数调用linux中的一些命令
刚才的第一个例子写的是我们用excel函数调用自己写的可执行文件,那么接下来的例子中,我们要调用linux中的ls命令,,调用date命令
首先利用whereis命令查出来两个个指令的路径,如下图所示
案例一:调用ls命令,代码如下
运行结果如下,可以看到,确实执行了ls的功能
案例二:调用date命令,代码如下
运行结果如下,可以看到,却是执行了date功能
二、execlp函数的使用
int execlp(const char *file, const char *arg, ...);
如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。(个人觉得环境变量可以理解为当前可执行文件的路径)
参数
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
execlp函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
1.验证按PATH环境变量,在它所指定的各目录中搜寻可执行文件。代码如下
这里我们的带一个参数是ps,它在bin路径下可以找到,然后我们的第二个参数是ps,就是要执行ps指令。
运行结果如下
2.如何修改环境变量
如果想要使用execlp函数,第一个参数还不想要写路径,那么你的第一个参数文件就必须在环境变量里。但是我们自己手写的文件是不会出现在系统默认的环境变量中的,如何让我我们写的文件出现在环境变量中呢?可以按照下面的操作
1 | 输入echo $PATH | 查看系统默认的环境变量 |
2 | 输入cd 你手写的文件对应的文件夹 | 转到对应的文件夹 |
输入pwd | 查看当前文件夹路径 | |
输入export PATH=$PATH: 当前文件夹路径 | 向系统默认的环境变量中添加新的文件路径 | |
输入echo $PATH | 查看新的环境变量 |
按照上述步骤,即可配置完成,如下图所示
但是要强调的是,这种自己配置的环境变量是临时的,当关闭Shell终端后就失效了。
我的echoarg文件是在jingcheng的文件夹下,当我将它文件路径配置到环境变量中后,就可以使用excelp函数了
运行结果如下
三、system函数的使用
system原型
int system(const char *command);
参数是一个字符串常量
system()函数的返回值如下:
成功,则返回进程的状态值;
当sh不能执行时,返回127;
失败返回-1;
这是从大佬的博客里拷贝下来的system源码
#include
#include
#include
#include
int system(const char * cmdstring)
{
? pid_t pid;
? int status;
? if(cmdstring == NULL){
?????
????? return (1);
? }
? if((pid = fork())<0){
??????? status = -1;
? }
? else if(pid == 0){
??? execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
??? -exit(127); //子进程正常执行则不会执行此语句
??? }
? else{
??????? while(waitpid(pid, &status, 0) < 0){
????????? if(errno != EINTER){
??????????? status = -1;
??????????? break;
????????? }
??????? }
??? }
??? return status;
}
要说的是execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);里的参数
sh -c 可执行文件路径就相当于可执行文件路径
意思就是sh -c ./a.out与./a.out等价
(char *)0就相当于NULL
通过观看源码,我们可以发现system函数中调用了execl函数,但是与execl不同的是,system使用后依旧会返回到原来的程序中去,去把原来的程序执行完。下面我们用代码验正一下。
个人对system函数理解:其实我感觉syetem就是一个变相的execl,为什么我会这样说呢看看下面这段代码
运行结果如下
还记得上文中的system的源码中的代码吗
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
cmdstring是一个字符串常量,对应于我写的./echoarg echoarg NUL。可以看到的是我写的字符串常量中给出了文件地址以及参数,而且虽然system函数调用了execl函数,但是我们使用system函数给参数的时候却不需要加上NULL(因为(char *)0就相当于NULL),而且我们写的一长串字符常量是通过空格被划分成了不同的参数,程序会自动识别。
本人能力有限,关于system函数就只能说这么多了,更多的细节可以参考文章开头大佬的博客。
四、popen函数的使用
FILE popen( const char *command, const char* mode )
参数说明:
Command:一个一个字符串常量,用法与system一样
mode:r代表读,w代表写
返回值:
如果调用成功,则返回一个读或者打开文件的指针,如果失败,返回NULL,具体错误要根据errno判断
int pclose (FILE* stream)
参数说明:
stream:popen返回的文件指针
返回值:
如果调用失败,返回 -1
作用:
popen() 函数用于创建一个管道:其内部实现为调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程这个进程必须由 pclose() 函数关闭。
相比于system,popen函数可以获取程序运行的输出结果。什么意思呢?说白了就是system函数只能将数据在命令终端打印出来,但是popen函数还可以将你执行的数据内容留下来。
比如在下面的程序里,我们调用了popen函数后返回fp,接着使用fread函数,通过fp找到执行的数据内容读到缓冲区ret里,然后我们就还可以读出来了。
还不明白的话看看下面这个图
下面是验证的代码
运行结果如下图所示,可以看到通过fread读出来了程序运行的输出结果。