exec 族函数和system 函数 (执行shell 命令)

exec族函数函数的作用:
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。

exec族函数定义:
  可以通过这个网站查询:linux函数查询
功能:
  在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数族:
  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[]);

exaclp函数带p,所以能通过环境变量PATH查找到可执行文件ps

返回值:
  exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。

————————————————
版权声明:本文为CSDN博主「云英」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014530704/article/details/73848573

execl 函数原型

int execl(const char *path, const char *arg0, ..., NULL);
execl("/bin/ls", "ls", "-l", "-a", NULL);
  1. “/bin/ls”
    作用:要执行的程序的完整路径
    说明:指定了 ls 命令在文件系统中的位置
    注意:execl 需要完整路径,不会自动搜索 PATH 环境变量
  2. “ls”
    作用:程序的 argv[0](命令名称)
    说明:通常是程序名本身,但可以任意命名
    示例:ps 命令查看进程时显示的就是这个名称
  3. “-l”
    作用:第一个命令行参数
    说明:长格式显示,包含详细信息
    效果:显示文件权限、所有者、大小、修改时间等
  4. “-a”
    作用:第二个命令行参数
    说明:显示所有文件(包括隐藏文件)
    效果:显示以 . 开头的隐藏文件
  5. NULL –> 不能漏
    作用:参数列表的结束标志
    说明:告诉函数参数列表到此为止
    必须性:绝对不能省略,否则会导致未定义行为

perror函数会打印错误信息

使用

//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    printf("before execl\n");
    if(execl("./bin/echoarg","echoarg","abc",NULL) == -1)
    {
        printf("execl failed!\n");

        perror("");//里面要放双引号或者双引号里面加点字符串,不能没有双引号
    }
    printf("after execl\n");
    return 0;
}

exec族函数配合fork使用

实现功能,当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。

代码 –> 子进程配置文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int data = 10;

    while(1)
    {
            printf("please input a data\n");
            scanf("%d",&data);
            if(data == 1)//当输入1
            {
                    pid = fork();//创建子进程
                    if(pid >0)//
                    {
                            wait(NULL);//阻塞父进程
                    }
                    if(pid == 0)//子进程来
                    {
                        int fdSrc;
                        char *readBuf = NULL;

                        fdSrc = open("config.txt",O_RDWR);//打开config.txt
                        int size = lseek(fdSrc,0,SEEK_END);//读取fdSrc文件的字节
                        lseek(fdSrc,0,SEEK_SET);//光标放在最前面

                        readBuf = (char *)malloc(sizeof(char)*size + 8);//读取的个数

                        int n_read = read(fdSrc, readBuf,size);//读取fdSrc的内容

                        char *p = strstr(readBuf,"LENG=");//查找LENG=
                        if(p == NULL)
                        {
                                printf("not found\n");
                                exit(-1);
                        }
                        p = p+strlen("LENG=");//移动指针到参数位置
                        *p = '5';//写入5

                        lseek(fdSrc,0,SEEK_SET);//光标放到最前面
                        int n_write = write(fdSrc,readBuf,strlen(readBuf));//写入

                        close(fdSrc);//关闭
                        exit(0);//退出
                    }
             }
            else
            {
                    printf("wait ,do nothing\n");
            }
    }

将这个文件 gcc 为 changData

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>

int main(int argc, char **argv)
{
    int fdSrc;
    char *readBuf = NULL;

    if(argc != 2)
    {
            printf("patatm error\n");
            exit(-1);
    }

    fdSrc = open(argv[1], O_RDWR);
    int size = lseek(fdSrc,0,SEEK_END);
    lseek(fdSrc,0,SEEK_SET);

    readBuf = (char *)malloc(sizeof(char)*size + 8);

    int n_read = read(fdSrc, readBuf, size);

    char *p = strstr(readBuf, "LENG=");//在&nbsp;`readBuf`&nbsp;字符串中搜索子串&nbsp;`"LENG="`
    if(p == NULL){
            printf("not found\n");
            exit(-1);
            }

    p = p + strlen("LENG=");//读取 LENG= 的个数 + p  
    *p = '5';//将光标位置的内容改为 5

    lseek(fdSrc,0,SEEK_SET);
    int n_write = write(fdSrc,readBuf,strlen(readBuf));

    close(fdSrc);
//      close(fdDes);


    return 0;
}

用这个代码直接调用 changData

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int data = 10;

    while(1)
    {
            printf("please input a data\n");
            scanf("%d",&data);
            if(data == 1)//当输入1
            {
                    pid = fork();//创建子进程
                    if(pid >0)//
                    {
                            wait(NULL);//阻塞父进程
                    }
                    if(pid == 0)//子进程来运行
                    {
                        execl("./changData","changData","config.txt",NULL);
                    }
             }
            else
            {
                    printf("wait ,do nothing\n");
            }
    }

解析 execl("./changData","changData","config.txt",NULL);

  • ./changData 要执行的程序的路径
  • changData 程序的 argv[0](命令名称)
  • config.txt 命令行参数
  • NULL 参数列表的结束标志 一定要加

system 函数 (执行shell 命令)

linux system函数详解 – 南哥的天下 – 博客园 参考这个
头文件

#include<stdlib.h>


定义函数

int system(const char * string);

函数说明
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
返回值
如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值。如果 system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。
system 函数就是调用封装好的exec函数

#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;
}

system 调用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int data = 10;

    while(1)
    {
        printf("please input a data\n");
        scanf("%d",&data);
        if(data == 1)
        {
            pid = fork();
            if(pid >0)
            {
                    wait(NULL);
            }
            if(pid == 0)
            {
                    // exec会用新程序替换当前进程
            //      execl("./changData","changData","config.txt",NULL);
                    // system会创建子进程执行命令,并等待命令完成
                    system("./changData config.txt");
            }
        }
        else
        {
            printf("wait ,do nothing\n");
         }
    }
    return 0;
image.png

重点 system和exec的区别

特性system() 函数exec() 函数族
执行后行为会返回继续执行后续代码不会返回,原进程被完全替换
进程关系创建子进程执行命令,父进程等待替换当前进程,不创建新进程
返回值返回命令的退出状态成功时不返回,失败返回-1
资源开销较大(需要创建子进程)较小(直接替换当前进程)

system

#include <stdlib.h>

int main() {
    printf("准备调用system...\n");

    // system会创建子进程执行命令,并等待命令完成
    int result = system("ls -l");

    // 命令执行完毕后,会继续执行这里的代码
    printf("system调用完成,返回值: %d\n", result);
    printf("这行代码会被执行\n");

    return 0;
}

输出示例

准备调用system...
总用量 0
-rwxr-xr-x 1 user user 0 Dec 1 10:00 test
system调用完成,返回值: 0
这行代码会被执行

exec

#include <unistd.h>

int main() {
    printf("准备调用exec...\n");

    // exec会用新程序替换当前进程
    execl("/bin/ls", "ls", "-l", NULL);

    // 如果exec成功,下面的代码永远不会执行!
    printf("这行代码不会被执行(除非exec失败)\n");
    perror("exec失败"); // 只有exec失败时才会执行

    return 0;
}

输出示例

准备调用exec...
总用量 0
-rwxr-xr-x 1 user user 0 Dec 1 10:00 test
(程序在此结束,不会返回原进程)

popen 函数

popen() 创建了一个管道,用于在程序和外部的 shell 命令之间建立双向数据通道,可以读取命令的输出或向命令发送输入。

原型

#include <stdio.h>

FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

参数说明

command

  • 要执行的 shell 命令字符串
  • 示例:"ls -l""grep hello""wc -l"

type

  • 指定数据流方向:
    • "r"读取命令的输出(从命令到程序)
    • "w"写入到命令的输入(从程序到命令)

返回值

  • 成功:返回指向 FILE 结构的指针
  • 失败:返回 NULL

FILE* 流指针

FILE* 是 C 语言中用于标准 I/O 操作的核心概念,它就像一个”文件操作的遥控器”,提供了对数据流的高级访问接口

一句话理解

FILE* 是一个指向 FILE 结构体的指针,它封装了文件操作的底层细节,为我们提供了统一的缓冲流式访问接口

FILE 结构体(通常实现)

// FILE 结构体的大致内容(实际实现因编译器而异)
typedef struct {
    int         _fd;       // 文件描述符
    char*       _buffer;   // 数据缓冲区
    size_t      _bufsize;  // 缓冲区大小
    int         _flags;    // 状态标志
    char*       _ptr;      // 当前缓冲区位置
    int         _cnt;      // 缓冲区剩余字节数
    // ... 其他字段
} FILE;

打开文件

FILE *fp = fopen("file.txt", "r");  // 打开文件获取流指针

常用流操作函数

函数作用示例
fopen()打开文件获取流指针FILE *fp = fopen("file.txt", "r")
fclose()关闭流fclose(fp)
fread()从流读取数据fread(buffer, 1, 100, fp)
fwrite()向流写入数据fwrite(data, 1, len, fp)
fprintf()格式化输出到流fprintf(fp, "Value: %d", 42)
fscanf()从流格式化输入fscanf(fp, "%d", &value)
fgets()从流读取一行fgets(line, 100, fp)
fputs()向流写入字符串fputs("Hello", fp)

代码

//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
        char ret[1024] = {0};// 创建 1024 字节的缓冲区并初始化为 0
        FILE *fp; // 声明文件流指针

        fp = popen("ps","r"); // 执行 "ps" 命令,以读取模式打开管道
        int nread = fread(ret,1,1024,fp);//从文件流`fp`中读取数据到缓冲区`ret

        printf("read ret %d byte,ret=%s\n",nread,ret);/要print打印,不打印么有显示
        return 0;
}
image.png
fp = popen("ps", "r");  // 执行 "ps" 命令,以读取模式打开管道
  • popen("ps", "r"):创建子进程执行 ps 命令
  • "r" 模式:表示从命令的输出中读取数据
  • 返回 FILE* 流指针,可以通过标准 I/O 函数读取命令输出
int nread = fread(ret, 1, 1024, fp);//从文件流`fp`中读取数据到缓冲区`ret并返回字节数给nread
  • ret:存储读取数据的缓冲区
  • 1:每个元素的大小(1 字节)
  • 1024:最多读取的元素数量
  • fp:输入流
  • nread:实际读取的字节数

重点:

  • 比system在应用中的好处:
    可以获取运行的输出结果
  • 与相似函数的对比
特性popen()system()exec()
获取输出✅ 可以读取命令输出❌ 不能获取输出❌ 不能获取输出
发送输入✅ 可以向命令发送输入❌ 不能发送输入❌ 不能发送输入
进程控制创建子进程,通过管道通信创建子进程,等待完成替换当前进程
返回值FILE* 流指针命令退出状态成功不返回
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇