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函数

  • 重定向https://blog.csdn.net/tiandc/article/details/81489447
#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

发布了106 篇原创文章 · 获赞 76 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/Creator_Ly/article/details/104145680