c语言调用shell命令一 popen使用以及获取命令返回值

      产品升级,新增网卡,原先的产品是arm平台,新网卡是mips平台,需要开发网卡的配置程序,该程序原计划是以守护进程的形式后台执行,不过测试过程中发现系统不是特别稳定,导致程序时不时奔溃下,一时半会儿无法解决,只能先给该程序加个壳,以系统调用的方式来执行,如果出现问题的话重复调用就可以了。

      以前在程序里调用系统命令的话,使用的是system()函数,不过system函数无法获取命令的输出,查了下资料,发现有个popen调用,和system类似,也能够执行系统命令,区别在于它能够获取命令的输出或者给系统命令传递参数,类似与管道的作用。

popen接口定义:

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

    popen函数会创建一个管道,并且创建一个子进程来执行shell,shell会创建一个子进程来执行command,
根据type的值不同,分成两种情况:
    如果type是r: command执行的标准输出,就会写入管道,从而被调用popen的进程读到,通过对popen返
回的FILE类型指针执行read或fgets操作,就可以读取到command的标准输出。
    如果type是w:调用popen的进程可以通过对FILE类型指针执行write、fputs等操作,负责往管道里面写
入,写入的内容经过管道传递给执行command的进程,作为命令的的输入。
    
    popen函数成功时,会返回stdio库封装的FILE类型的指针,失败时会返回NULL,并且设置errno,
常见的失败有fork失败、pipe失败,或者分配内存失败。
    I/O结束后,可以调用pclose函数来关闭管道,并且等待子进程的退出。pclose函数成功时会返回
子进程shell的终止状态。popen函数和system函数类似,如果command对应命令无法执行,就如同
执行了exit(127)一样,如果发生其它错误,pclose函数则返回-1.可以从errno中获取到失败的原因。

命令执行后需要获取命令的返回值,可以通过如下几个宏来获取:

1. 进程正常退出
WIFEXITED(status) : 如果子进程正常退出,则返回true,否则返回false
WEXITSTATUS(status):如果子进程正常退出,则本宏用来获取进程的退出状态

2. 进程收到信号,导致退出
WIFSIGNALED(status) : 如果进程是被信号杀死的,则返回true,否则返回false
WTREMSIG(status):如果进程是被信号杀死的,则返回杀死进程信号的值
WCOREDUMP(status) : 如果子进程产生了core dump,则返回true,否则返回false

测试用例:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<sys/wait.h>
#include<signal.h>

#define MAX_LINE_SIZE 8192

/* 打印shell返回结果 */
void print_wait_exit(int status)
{
    /* shell正常退出,即便命令执行异常,shell也有可能是正常退出的 */
    if(WIFEXITED(status))                                
    {
        printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
    }
    /* shell异常退出 */
    else if(WIFSIGNALED(status))
    {
        printf("abnormal terminatio, signal number = %d\n", WTERMSIG(status));
    }    
}

int main(int argc, char *argv[])
{
    FILE *fp = NULL;
    char command[MAX_LINE_SIZE], buffer[MAX_LINE_SIZE];

    if(argc != 2)
    {
        fprintf(stderr, "Usage : %s filename \n", argv[0]);
        exit(1);
    }

    snprintf(command, sizeof(command), "%s", argv[1]);
    fp = popen(command, "r");
    if (fp == NULL)
    {
        fprintf(stderr, "popen failed (%s)", strerror(errno));
        exit(2);        
    }

    while (fgets(buffer, MAX_LINE_SIZE,fp) != NULL)
    {
        fprintf(stdout, "%s",buffer);
    }

    int ret = pclose(fp);
    if (ret == 127)
    {
        fprintf(stderr, "bad command :%s \n",command);
        exit(3);
    }
    else if (ret == -1)
    {
        fprintf(stderr, "failed to get child status :%s \n",strerror(errno));
        exit(4);

    }
    else
    {
        print_wait_exit(ret);
    }
    exit(0);
}

运行截图:

参考资料:

1. 《Linux环境编程 从应用到内核》高峰,李彬著

在C语言中,你可以通过标准库函数`system()`或者`popen()`、`pclose()`函数组合来调用Shell命令获取返回结果。以下是几种常见方法: 1. **使用`system()`函数**: ```c #include <stdio.h> int main() { system("your_command_here"); return 0; } ``` 这会直接执行命令,但`system()`不会返回命令的输出结果,它通常用于简单的命令执行。 2. **使用`popen()`和`pclose()`**: ```c #include <stdio.h> #include <unistd.h> #define BUFFER_SIZE 1024 int main() { FILE *pipe = popen("your_command_here", "r"); // 'r' for reading output if (pipe != NULL) { char buffer[BUFFER_SIZE]; size_t read_size; while ((read_size = fread(buffer, 1, sizeof(buffer), pipe)) > 0) { buffer[read_size - 1] = '\0'; // Ensure null termination printf("%s", buffer); } pclose(pipe); // Close the pipe after reading } else { perror("Error opening pipe"); } return 0; } ``` 这里,`popen()`创建个管道连接到命令的输入和输出,然后`fread()`从管道读取内容。 3. **使用`execlpe()`或`execlp()`**: 这两个函数直接替换进程,适合执行独立的程序,并不是原地返回结果。例如: ```c #include <execinfo.h> int main() { execlp("your_command_here", "your_command_here", NULL); // Third parameter is optional arguments perror("Failed to execute command"); exit(1); } ``` 注意,上述方法可能会有安全风险,尤其是涉及用户输入的情况,因为它们可能导致命令注入攻击。务必妥善处理用户提供的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值