C语言执行shell命令(system exec popen pipe)

我们在C语言里面有时候需要执行一些shell命令,或者通过shell命令获取一些返回的数据。

无需返回执行结果 system/exec

如果执行命令不要返回,那最常用的就是直接使用system

sysytem("reboot")

可以使用exec家族的函数,失败返回-1

#include 

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[]);

其实system的实现方式就是调用的execl函数

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 != EINTR)
            {
                status = -1;
                break;
            }
        }
    }
    return(status);
 } 

需要返回执行结果-popen

有时候我们需要返回数据信息,如执行ls -l,这时就不能使用system

需要使用popen来实现了,popen总是和pclose一起出现被使用的。

popen() 创建一个管道,通过fork或者invoke一个子进程,然后执行command。

返回值在标准IO流中,由于是在管道之中,因此数据流是单向的,command只能产生stdout或者读取stdin,因此type只有两个值:‘w’或‘r’。

r表示command从管道中读取数据流,而w表示command的stdout输出到管道中。command无法同时读取和输出。popen返回该FIFO数据流的指针。

如下:

一般还要过滤下&符号

#define CMD_LEN         128
#define BUF_LEN         1400

void execute_cmd(const char* cmd, char* buf, int buf_len)
{
    int     status = -1;
    FILE*   fp;
    char*   p;
    char    pwd[CMD_LEN] = "";
    char unpadding[CMD_LEN];
    char fgets_buf[512];
    int len;
    int end;

    if (buf == NULL || buf_len <= 0)
    {
        return;
    }
    
    len = strlen(cmd);
    end = (int)cmd[len - 1]; 

    /* unpadding */
    memset(unpadding, 0, sizeof(unpadding));
    if (end < len) {
        memcpy(unpadding, cmd, len - end);
    } else {
        memcpy(unpadding, cmd, len);
    }   

    if ((p = strchr(unpadding, '\n')) != NULL) {
        strncpy(pwd, unpadding, p - unpadding);
        ++p;
    }   
    else
        exit(0);

    printf("execute cmd = %s\n", p);

    if ('&' == p[strlen(p) - 1]) {
        status = system(p);
        sprintf(buf, "%d", WEXITSTATUS(status));
    } else {
        fp = popen(p, "r");

        if (fp != NULL)
        {
            while (fgets(fgets_buf, sizeof(fgets_buf), fstream))
            {
                if (len < sizeof(buf))
                {
                    len += snprintf(buf+len, sizeof(buf)-len, "%s", buff);
                }
            }
            pclose(fp);
        }
    }
}

需要返回执行结果-匿名管道pipe

使用管道来获取执行shell命令返回的信息,一般流程如下

  • 1.创建进程,创建匿名管道
  • 2.子进程使用dup函数复制描述符将shell命令行标准输出绑定到管道的写端
  • 3.父进程从管道的读端读取数据

pipe函数

  • 所需头文件:#include
  • 函数原型:int pipe(int fd[2]);
  • 返回值:成功返回0,出错返回-1

dup函数

  • 重定向https://blog.csdn.net/tiandc/article/details/81489447
#include 
#include
#include
#include
int main()
{
    int fpipe[2] = {0};
    pid_t fpid;
    char massage[1000] = {0};
    memset(massage, 0, 20);
    if (pipe(fpipe) < 0)
    {
        printf("Create pipe error!\n");
    }
    fpid = fork();
    if (fpid == 0)
    {
        close(fpipe[0]);
        dup2(fpipe[1],STDOUT_FILENO);
        system("ls");
    }
    else if (fpid > 0)
    {
        wait(NULL);
        printf("this is father,recieve:");
        fflush(stdout);
        close(fpipe[1]);
        read(fpipe[0], massage, 1000);
        printf("%s\n",massage);
    }
    else
    {
        printf("create fork error!\n");
    }
    return 0;
}

另一种思路-有名管道fifo

不管是sysytem还是popen的使用,内部其实都会fork一个进程,这其实是很耗系统资源的,现在公司实现了一个方法,不过我一直也没理解为什么可以不耗资源呢。

实现的形式是这样的

一个单独的vshd.sh脚本,一开机就后台运行,然后创建fifo,一直在等待接收数据。

C语言里面每次要执行命令时,就把命令发给vshd.sh监听的管道,再创建一个新的管道用来等待接收vshd.sh将执行完的命令返回给C语言。

这样每次要执行命令时都不调用system/popen,而是通过管道发送给vshd.sh后台脚本来执行。

参考资料
https://blog.csdn.net/qq_27664167/article/details/82194391

你可能感兴趣的:(C语言执行shell命令(system exec popen pipe))