我們在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