Linux下system与popen函数
来源:互联网 发布:iptables 禁用端口 编辑:程序博客网 时间:2024/06/05 19:16
Linux下使用system()函数一定要谨慎
1
#include <stdlib.h>
2
int
system
(
const
char
*command);
system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.
01
int
system
(
const
char
* cmdstring)
02
{
03
pid_t pid;
04
int
status;
05
06
if
(cmdstring == NULL)
07
{
08
return
(1);
//如果cmdstring为空,返回非零值,一般为1
09
}
10
11
if
((pid = fork())<0)
12
{
13
status = -1;
//fork失败,返回-1
14
}
15
else
if
(pid == 0)
16
{
17
execl(
"/bin/sh"
,
"sh"
,
"-c"
, cmdstring, (
char
*)0);
18
_exit(127);
// exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~
19
}
20
else
//父进程
21
{
22
while
(waitpid(pid, &status, 0) < 0)
23
{
24
if
(
errno
!= EINTR)
25
{
26
status = -1;
//如果waitpid被信号中断,则返回-1
27
break
;
28
}
29
}
30
}
31
32
return
status;
//如果waitpid成功,则返回子进程的返回状态
33
}
01
int
status;
02
if
(NULL == cmdstring)
//如果cmdstring为空趁早闪退吧,尽管system()函数也能处理空指针
03
{
04
return
XXX;
05
}
06
status =
system
(cmdstring);
07
if
(status < 0)
08
{
09
printf
(
"cmd: %s\t error: %s"
, cmdstring,
strerror
(
errno
));
// 这里务必要把errno信息输出或记入Log
10
return
XXX;
11
}
12
13
if
(WIFEXITED(status))
14
{
15
printf
(
"normal termination, exit status = %d\n"
, WEXITSTATUS(status));
//取得cmdstring执行结果
16
}
17
else
if
(WIFSIGNALED(status))
18
{
19
printf
(
"abnormal termination,signal number =%d\n"
, WTERMSIG(status));
//如果cmdstring被信号中断,取得信号值
20
}
21
else
if
(WIFSTOPPED(status))
22
{
23
printf
(
"process stopped, signal number =%d\n"
, WSTOPSIG(status));
//如果cmdstring被信号暂停执行,取得信号值
24
}
到于取得子进程返回值的相关介绍可以参考另一篇文章:http://my.oschina.net/renhc/blog/35116
system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆。这里推荐使用popen()函数替代,关于popen()函数的简单使用也可以通过上面的链接查看。
popen()函数较于system()函数的优势在于使用简单,popen()函数只返回两个值:
成功返回子进程的status,使用WIFEXITED相关宏就可以取得command的返回结果;
失败返回-1,我们可以使用perro()函数或strerror()函数得到有用的错误信息。
这篇文章只涉及了system()函数的简单使用,还没有谈及SIGCHLD、SIGINT和SIGQUIT对system()函数的影响,事实上,之所以今天写这篇文章,是因为项目中因有人使用了system()函数而造成了很严重的事故。现像是system()函数执行时会产生一个错误:“No child processes”。
linux下代替system的基于管道的popen和pclose函数
标准I/O函数库提供了popen函数,它启动另外一个进程去执行一个shell命令行。
这里我们称调用popen的进程为父进程,由popen启动的进程称为子进程。
popen函数还创建一个管道用于父子进程间通信。父进程要么从管道读信息,要么向管道写信息,至于是读还是写取决于父进程调用popen时传递的参数。下在给出popen、pclose的定义:
01
#include <stdio.h>
02
/*
03
函数功能:popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。
04
参数type可使用“r”代表读取,“w”代表写入。
05
依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。
06
随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中
07
返回值:若成功则返回文件指针,否则返回NULL,错误原因存于errno中
08
*/
09
FILE
* popen(
const
char
* command,
const
char
* type);
10
11
/*
12
函数功能:pclose()用来关闭由popen所建立的管道及文件指针。参数stream为先前由popen()所返回的文件指针
13
返回值:若成功返回shell的终止状态(也即子进程的终止状态),若出错返回-1,错误原因存于errno中
14
*/
15
int
pclose(
FILE
* stream);
下面通过例子看下popen的使用:
假如我们想取得当前目录下的文件个数,在shell下我们可以使用:
1
ls
|
wc
-l
我们可以在程序中这样写:
01
/*取得当前目录下的文件个数*/
02
#include <stdio.h>
03
#include <stdlib.h>
04
#include <errno.h>
05
#include <sys/wait.h>
06
07
#define MAXLINE 1024
08
09
int
main()
10
{
11
char
result_buf[MAXLINE], command[MAXLINE];
12
int
rc = 0;
// 用于接收命令返回值
13
FILE
*fp;
14
15
/*将要执行的命令写入buf*/
16
snprintf(command,
sizeof
(command),
"ls ./ | wc -l"
);
17
18
/*执行预先设定的命令,并读出该命令的标准输出*/
19
fp = popen(command,
"r"
);
20
if
(NULL == fp)
21
{
22
perror
(
"popen执行失败!"
);
23
exit
(1);
24
}
25
while
(
fgets
(result_buf,
sizeof
(result_buf), fp) != NULL)
26
{
27
/*为了下面输出好看些,把命令返回的换行符去掉*/
28
if
(
'\n'
== result_buf[
strlen
(result_buf)-1])
29
{
30
result_buf[
strlen
(result_buf)-1] =
'\0'
;
31
}
32
printf
(
"命令【%s】 输出【%s】\r\n"
, command, result_buf);
33
}
34
35
/*等待命令执行完毕并关闭管道及文件指针*/
36
rc = pclose(fp);
37
if
(-1 == rc)
38
{
39
perror
(
"关闭文件指针失败"
);
40
exit
(1);
41
}
42
else
43
{
44
printf
(
"命令【%s】子进程结束状态【%d】命令返回值【%d】\r\n"
, command, rc, WEXITSTATUS(rc));
45
}
46
47
return
0;
48
}
$ gcc popen.c
$ ./a.out
命令【ls ./ | wc -l】 输出【2】
命令【ls ./ | wc -l】子进程结束状态【0】命令返回值【0】
上面popen只捕获了command的标准输出,如果command执行失败,子进程会把错误信息打印到标准错误输出,父进程就无法获取。比如,command命令为“ls nofile.txt” ,事实上我们根本没有nofile.txt这个文件,这时shell会输出“ls: nofile.txt: No such file or directory”。这个输出是在标准错误输出上的。通过上面的程序并无法获取。
注:如果你把上面程序中的command设成“ls nofile.txt”,编译执行程序你会看到如下结果:
$ gcc popen.c
$ ./a.out
ls: nofile.txt: No such file or directory
命令【ls nofile.txt】子进程结束状态【256】命令返回值【1】
需要注意的是第一行输出并不是父进程的输出,而是子进程的标准错误输出。
有时子进程的错误信息是很有用的,那么父进程怎么才能获取子进程的错误信息呢?
这里我们可以重定向子进程的错误输出,让错误输出重定向到标准输出(2>&1),这样父进程就可以捕获子进程的错误信息了。例如command为“ls nofile.txt 2>&1”,输出如下:
命令【ls nofile.txt 2>&1】 输出【ls: nofile.txt: No such file or directory】
命令【ls nofile.txt 2>&1】子进程结束状态【256】命令返回值【1】
附:子进程的终止状态判断涉及到的宏,设进程终止状态为status.
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。
WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。
WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。
对于linux下system()函数的深度理解(整理)
这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同的system()函数,直接在shell下输入system()函数中调用的命令也都一切正常.就没理这个bug,以为是其他的代码影响到这个,或是内核驱动文件系统什么的异常导致,昨天有出现了这个问题,就随手百了一下度,问题出现了,很多人都说system()函数要慎用要少用要能不用则不用,system()函数不稳定?
#include int main(){ system("mkdir $HOME/.SmartPlatform/"); system("mkdir $HOME/.SmartPlatform/Files/"); system("cp mainnew.cpp $HOME/.SmartPlatform/Files/"); return 0; } 下面我们来看看system函数的源码: #include #include #include #include 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 != EINTER){ status = -1; break; } } } return status; }
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成功,则返回子进程的返回状态 }仔细看完这个system()函数的简单实现,那么该函数的返回值就清晰了吧,那么什么时候system()函数返回0呢?只在command命令返回0时。看一下该怎么监控system()函数执行状态 这里给我出的做法:
int status; if(NULL == cmdstring) //如果cmdstring为空趁早闪退吧,尽管system()函数也能处理空指针 { return XXX; } status = system(cmdstring); if(status < 0) { printf("cmd: %s\t error: %s", cmdstring, strerror(errno)); // 这里务必要把errno信息输出或记入Log return XXX; } if(WIFEXITED(status)) { printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); //取得cmdstring执行结果 } else if(WIFSIGNALED(status)) { printf("abnormal termination,signal number =%d\n", WTERMSIG(status)); //如果cmdstring被信号中断,取得信号值 } else if(WIFSTOPPED(status)) { printf("process stopped, signal number =%d\n", WSTOPSIG(status)); //如果cmdstring被信号暂停执行,取得信号值 }
int pox_system(const char*cmd_line) { returnsystem(cmd_line); } 函数调用: int ret =0; ret = pox_system("gzip -c/var/opt/I00005.xml >/var/opt/I00005.z"); if(0 !=ret) { Log("zipfile failed\n"); }
int ret =0; ret =pox_system("gzip -c /var/opt/I00005.xml >/var/opt/I00005.z"); if(0 !=ret) { Log("zipfile failed: %s\n", strerror(errno));//尝试打印出系统错误信息 }
typedef void(*sighandler_t)(int); int pox_system(const char*cmd_line) { int ret =0; sighandler_told_handler; old_handler = signal(SIGCHLD,SIG_DFL); ret =system(cmd_line); signal(SIGCHLD,old_handler); returnret; }
if(signal(SIGCHLD, SIG_IGN) ==SIG_ERR) { return-1; } else{ return0; }其他思考我们公司的代码使用SVN进程管理的,到目前为止有很多branch,逐渐的,几乎每个branch都出现了上面的问题,于是我逐个在各个branchc上fix这个问题,几乎忙了一天,因为有的branch已被锁定,再想merge代码必须找相关负责人说明问题的严重性,还要在不同的环境上测试,我边做这些边想,系统这样升级合适吗?首先,由于系统的升级导致我们的代码在测试时发现问题,这时再急忙去fix,造成了我们的被动,我想这是他们的一个失误。你做的升级必须要考虑到对其他team的影响吧?何况你做的是系统升级。升级前需要做个风险评估,对可能造成的影响通知大家,这样才职业嘛。再者,据他们的说法,修改信号处理方式是为了避免僵尸进程,当然初衷是好的,但这样的升级影响了一些函数的使用方式,比如system()函数、wait()函数、waipid()、fork()函数,这些函数都与子进程有关,如果你希望使用wait()或waitpid()对子进程收尸,那么你必须使用上面介绍的方式:在调用前(事实上是fork()前)将SIGCHLD信号置为SIG_DFL处理方式,调用后(事实上wait()/waitpid()后)再将信号处理方式设置为从前的值。你的系统升级,强制大家完善代码,确实提高了代码质量,但是对于这种升级我不是很认同,试想一下,你见过多少fork()->waitpid()前后都设置SIGCHLD信号的代码?
#include #include #include #include #defineMAXLINE 1024 intmain() { char result_buf[MAXLINE],command[MAXLINE]; int rc = 0; //用于接收命令返回值 FILE*fp; snprintf(command,sizeof(command), "ls ./ | wc -l"); fp =popen(command, "r"); if(NULL ==fp) { perror("popen执行失败!"); exit(1); } while(fgets(result_buf,sizeof(result_buf), fp) != NULL) { if('\n' ==result_buf[strlen(result_buf)-1]) { result_buf[strlen(result_buf)-1] ='\0'; } printf("命令【%s】 输出【%s】\r\n",command, result_buf); } rc =pclose(fp); if(-1 ==rc) { perror("关闭文件指针失败"); exit(1); } else { printf("命令【%s】子进程结束状态【%d】命令返回值【%d】\r\n",command, rc, WEXITSTATUS(rc)); } return0; }
nt my_system(const char *cmd) { FILE *fp; int res; charbuf[1024]; if (cmd ==NULL) { printf("my_system cmd isNULL!\n"); return-1; } if ((fp = popen(cmd, "r") ) ==NULL) { perror("popen"); printf("popen error: %s/n",strerror(errno)); return -1; } else { while(fgets(buf, sizeof(buf),fp)) { printf("%s",buf); } if ( (res = pclose(fp)) ==-1) { printf("close popen filepointer fp error!\n"); return res; } else if (res ==0) { returnres; } else { printf("popen res is :%d\n",res); return res; } } }
- Linux下system与popen函数
- linux中popen() 函数与system () 详解
- linux命令C开发下使用常用函数system与popen开销比较
- popen 与system函数笔记
- linux下的popen()函数
- system与popen函数的效率
- system与popen函数的效率 .
- system与popen函数的效率
- linux下代替system的基于管道的popen和pclose函数
- linux下代替system的基于管道的popen和pclose函数
- linux下代替system的基于管道的popen和pclose函数
- linux下代替system的基于管道的popen和pclose函数
- linux下代替system的基于管道的popen和pclose函数
- popen system exec函数
- linux c语言 system系统调用与popen的使用
- linux popen函数
- Linux popen()函数使用
- linux popen函数
- 图片加载框架简单介绍<二> Picasso 的基本使用
- 音视频编解码开发经验2
- 利用MediaCodec对音频编解码
- NewPlan
- eclipse汉化教程
- Linux下system与popen函数
- URI与URL的区别
- PHP环境下Memcache的使用方法
- 单例模式
- JSP获取当前页面中的变量值
- 欢迎使用CSDN-markdown编辑器
- Java中getResourceAsStream的用法
- 将软件添加到右键菜单
- WIN10 删除文件 找不到该项目