概要
Linux下的C编程有以下几种方法可以执行shell命令:
- popen()函数
- system()函数
- exec函数簇
popen(建立管道I/O)
函数原型
FILE *popen(const char cmd,const char type); //若成功返回文件指针,出错则返回NULL。
参数:
- cmd
是一个指向以NULL结束的shell命令字符串的指针
。这行命令将被传到bin/sh并使用-c标志,shell将执行这个命令。
可以通过这个管道执行标准输入输出操作,这个管道必须由pclose()函数关闭,而不是fclose()函数(若使用fclose则会产生僵尸进程)。
pclose()函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。
如果shell不能被执行,则pclose()返回的终止状态与shell已执行exit一样。
- type
只能是读或者写
中的一种,得到的返回值(标准I/O流)也具有和type相应的只读或只写类型。
如果type=r
,那么该管道的方向为:子进程的stdout到父进程的FILE指针,即连接到cmd的标准输出;
如果type=w
,那么管道的方向为:父进程的FILE指针到子进程的stdin,即连接到cmd的标准输入。
返回值:
如果调用fork()或pipe()失败,或者不能分配内存将返回
NULL
,否则返回标准I/O流
。 popen()没有为内存分配失败设置errno值。
如果调用fork()或pipe()时出现错误,errno被设为相应的错误类型。
如果type参数不合法,errno将返回EINVAL。
流程:
- 创建一个匿名管道,
- 调用fork()或者invoke()产生一个子进程,
- 接着关闭管道的不使用端,
- 子进程执行cmd指向的应用程序或者命令。
- 执行完该函数后父进程和子进程之间生成一条管道,
- 函数返回值为FILE结构指针,该指针作为管道的一端,为父进程所拥有.**
子进程则拥有管道的另一端,该端口为子进程的stdin或者stdout。
这个流是单向的(只能用于读或写),向这个流写内容相当于写入该命令的标准输入,命令的标准输出和调用popen()的进程相同,
与之相反的,从流中读数据相当于读取命令的标准输出,命令的标准输入和调用popen()的进程相同。
system(执行shell 命令)
system()通过调用
/bin/sh -c
命令执行command
中指定的命令,并在命令完成后返回。
在执行命令期间,SIGCHLD
将被阻塞,SIGINT
和SIGQUIT
将被忽略<意思是进程收到这两个信号后没有任何动作>。
函数原型
#include <stdlib.h> int system(const char *command); //返回是否成功.
返回值:
如果出现错误(例如fork(2) failed),返回的值为
-1
,否则返回命令的状态
。
后一种返回状态采用wait(2)中指定的格式.因此,命令的退出代码将是WEXITSTATUS(status)。
- 如果
/bin/sh
无法执行,则退出状态将是exit(127)的命令的退出状态。- 如果
command
的值为NULL,那么system()在shell可用时返回非零,如果shell不可用则返回零。
源码实现:
int system(const char * cmdstring) { pid_t pid; int status; if(cmdstring == NULL) { return (1); //如果cmdstring为空,返回非零值,一般为1 } if((pid = fork())<0) { status = -1; //fork失败,返回-1 } else if (pid == 0) { execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); _exit(127); // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在 } else { //父进程 while(waitpid(pid, &status, 0) < 0) { if(errno != EINTR) { status = -1; //如果waitpid被信号中断,则返回-1 break; } } } return status; //如果waitpid成功,则返回子进程的返回状态 }
popen()与system()之间的区别
执行过程
popen
相当于是先创建一个管道,fork,关闭管道的一端,执行exec,返回一个标准的io文件指针.system
相当于是先后调用了fork, exec,wait来执行外部命令.
阻塞关系
popen
本身是不阻塞的,要通过标准io的读取使它阻塞。
不会等待子进程的结束并杀死子进程,即不会管理进程。system
本身就是阻塞的。
会自动对进程进行管理,无需我们再去对进程进行管理。
返回值
popen
会将执行的结果返回到buf中。system
不会返回执行的结果,只是会返回执行是否成功。
功能
popen ()
可以控制进程的输入或输出文件流。system ()
没有,如果不需要访问进程的I / O,则可以使用 system () 。