exec族函数
exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe
函数原型
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);//常用
int execlp(const char *file, const char *arg, ...);//常用
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);//常用
int execvp(const char *file, char *const argv[]);//常用
int execvpe(const char *file, char *const argv[],char *const envp[]);
返回值
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
为什么要用exec族
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
execl
l : 使用参数列表
int execl(const char *path, const char *arg, ...);
示例1
//achoarg.c文件
#include<stdio.h>
int main(int argc,char **argv)
{
int i=0;
for(i;i<argc;i++){
printf("argv[%d]%s\n",i,argv[i]);
}
return 0;
}
//exec.c文件
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("before execl\n");
if(execl("./achoarg","achoarg","abc",NULL)==-1){
printf("execl failed\n");
perror("why");//打印出错误原因
}
printf("after exexl\n");
return 0;
}
结果:
before execl
argv[0]achoarg
argv[1]abc
//为什么没有 after exexl 这句话打印?
//文件echoarg的作用是打印命令行参数。然后再编译execl.c并执行execl可执行文件。用execl 找到并执行echoarg,将当前进程main替换掉,所以”after execl” 没有在终端被打印出来。
示例2
int main()
{
printf("before execl\n");
if(execl("/bin/ls","ls","-l",NULL)==-1){
printf("execl failed\n");
perror("why");
}
printf("after exexl\n");
return 0;
}
执行结果就跟“ls -l”命令 的结果一样
注:
ls指令的地址 可以用“whereis ls ” 来查看
示例3
int main()
{
printf("this pro is system date\n");
if(execl("/bin/date","date",NULL)==-1){
printf("execl failed\n");
perror("why");
}
printf("after exexl\n");
return 0;
}
execlp
p:使用文件名,并从PATH环境进行寻找可执行文件
(如果使用的可执行文件在PATH里,则使用execlp可以不用详细描述绝对路径)
echo $PATH 可以直接打印出环境变量
如果想要在环境变量添加路径:expor PATH= $PATH:需要添加的路径(pwd获得)
示例
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("this pro is system date\n");
if(execlp("date","date",NULL)==-1){
//这时候第一个参数的date不用写详细路径了
printf("execl failed\n");
perror("why");
}
printf("after exexl\n");
return 0;
}
execv和execvp
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
execv
int main()
{
printf("this pro is system date\n");
char *arvg[]={"date",NULL};
// 定义一个指向各个参数的指针数组
if(execv("/bin/date",arvg)==-1){
printf("execl failed\n");
perror("why");
}
printf("after exexl\n");
return 0;
}
execvp
同理就是第一个参数的路径在环境变量中,就可以直接写名字
int main()
{
printf("this pro is system date\n");
char *arvg[]={"date",NULL};
if(execvp("date",arvg)==-1){
//第一个参数直接用 date 不需要加路径了
printf("execl failed\n");
perror("why");
}
printf("after exexl\n");
return 0;
}
exec和fork相结合的示例
已知change在当前路径,它可以对当前目录中的config.txt文件里面的一个指定字符进行修改。 “./change config.txt”
int main()
{//不断检测用户输入,如果输入为1,则创建子进程来调用exec函数
pid_t pid;
int data;
int fd;
while(1){
printf("please imput data:\n");
scanf("%d",&data);
if(data==1){
pid=fork();
if(pid>0){
wait(NULL);
//父进程等待子进程exec执行完,才会继续往下执行
printf("*****\n");
}
if(pid==0){
printf("%d\n",getpid());
execl("./change","./change","config.txt",NULL);
//用exec来执行change 从而实现更改字符的功能
}
}else{
printf("do nonthing!\n");
}
}
return 0;
}
分析:好处就是可以使代码简洁,不需要将change的代码复制到子进程当中去。
system
函数原型
int system(const char * string);
注:
执行指令 ./a.out
或者sh -c ./a.out
返回值
system()函数的返回值如下:
成功,则返回进程的状态值;
当sh不能执行时,返回127;
失败返回-1;
示例1(在上一段代码只需要修改一行代码)
execl("./change","./change","config.txt",NULL);
改成
system("./change config.txt");//直接写正常的执行指令格式就可
执行的效果和下面在终端执行的指令得到的效果一样
示例2
int main()
{
printf("this pro is system date\n");
int status=system("date");
printf("%d\n",status);
printf("%d\n",WEXITSTATUS(status));//返回的值需要解析
printf("after system\n");//和exec的区别就是,调用system后还是会执行这行代码
//而exec函数调用成功后,mian函数被替换了,直接执行exec里面运行的程序。
return 0;
}
popen
函数原型:
FILE *popen(const char *command, const char *type);
popen比system的优点是,popen可以获取运行的输出结果
参数说明:
command:
是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用 -c 标志,shell 将执行这个命令。
type:
如果 type 是 “r” 则表示读
如果 type 是 “w” 则表示写
r和w是相对command的管道而言的。r表示command从管道中读入,w表示 command通过管道输出到它的stdout
返回值:
若成功则返回文件指针,否则返回NULL,错误原因存于errno中
system的结果
int main()
{
printf("this pro is system date\n");
int c;
c=system("ps");
printf("c=%d\n",c);
return 0;
}
popen的结果
int main()
{
printf("this pro is system date\n");
FILE *fp;
char readBuf[1024]={0};
fp=popen("ps","r");
//如果参数为w,则会直接把内容输出在终端界面上
int n=fread(readBuf,sizeof(char),1024,fp);
printf("readbuf=\n%s\n",readBuf);
printf("%d byt\n",n);
pclose(fp);
return 0;
}