C語言執(zhí)行shell命令(system exec popen pipe)

我們在C語言里面有時候需要執(zhí)行一些shell命令,或者通過shell命令獲取一些返回的數(shù)據(jù)。

無需返回執(zhí)行結(jié)果 system/exec

如果執(zhí)行命令不要返回,那最常用的就是直接使用system

sysytem("reboot")

可以使用exec家族的函數(shù),失敗返回-1

#include <unistd.h>

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的實現(xiàn)方式就是調(diào)用的execl函數(shù)

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

需要返回執(zhí)行結(jié)果-popen

有時候我們需要返回數(shù)據(jù)信息,如執(zhí)行ls -l,這時就不能使用system

需要使用popen來實現(xiàn)了,popen總是和pclose一起出現(xiàn)被使用的。

popen() 創(chuàng)建一個管道,通過fork或者invoke一個子進程,然后執(zhí)行command。

返回值在標準IO流中,由于是在管道之中,因此數(shù)據(jù)流是單向的,command只能產(chǎn)生stdout或者讀取stdin,因此type只有兩個值:‘w’或‘r’。

r表示command從管道中讀取數(shù)據(jù)流,而w表示command的stdout輸出到管道中。command無法同時讀取和輸出。popen返回該FIFO數(shù)據(jù)流的指針。

如下:

一般還要過濾下&符號

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

需要返回執(zhí)行結(jié)果-匿名管道pipe

使用管道來獲取執(zhí)行shell命令返回的信息,一般流程如下

  • 1.創(chuàng)建進程,創(chuàng)建匿名管道
  • 2.子進程使用dup函數(shù)復(fù)制描述符將shell命令行標準輸出綁定到管道的寫端
  • 3.父進程從管道的讀端讀取數(shù)據(jù)

pipe函數(shù)

  • 所需頭文件:#include<unistd.h>
  • 函數(shù)原型:int pipe(int fd[2]);
  • 返回值:成功返回0,出錯返回-1

dup函數(shù)

#include<stdio.h> 
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
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的使用,內(nèi)部其實都會fork一個進程,這其實是很耗系統(tǒng)資源的,現(xiàn)在公司實現(xiàn)了一個方法,不過我一直也沒理解為什么可以不耗資源呢。

實現(xiàn)的形式是這樣的

一個單獨的vshd.sh腳本,一開機就后臺運行,然后創(chuàng)建fifo,一直在等待接收數(shù)據(jù)。

C語言里面每次要執(zhí)行命令時,就把命令發(fā)給vshd.sh監(jiān)聽的管道,再創(chuàng)建一個新的管道用來等待接收vshd.sh將執(zhí)行完的命令返回給C語言。

這樣每次要執(zhí)行命令時都不調(diào)用system/popen,而是通過管道發(fā)送給vshd.sh后臺腳本來執(zhí)行。

參考資料
https://blog.csdn.net/qq_27664167/article/details/82194391

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容