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

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

无需返回执行结果 system/exec

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

sysytem("reboot")

可以使用exec家族的函数,失败返回-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的实现方式就是调用的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<unistd.h>
  • 函数原型:int pipe(int fd[2]);
  • 返回值:成功返回0,出错返回-1

dup函数

#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的使用,内部其实都会fork一个进程,这其实是很耗系统资源的,现在公司实现了一个方法,不过我一直也没理解为什么可以不耗资源呢。

实现的形式是这样的

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

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

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容