linux的system () 函数详解及总结

来源:互联网 发布:游戏锁定fps软件 编辑:程序博客网 时间:2024/05/19 12:39
linux的system () 函数详解
 

system(执行shell 命令)
相关函数
        fork,execve,waitpid,popen
表头文件
        #i nclude<stdlib.h>
定义函数
        int system(const char * string);
函数说明
        system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命>令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
返回值
=-1:出现错误 
=0:调用成功但是没有出现子进程 
>0:成功退出的子进程的id
        如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值>。
如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为 system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。
附加说明
        在编写具有SUID/SGID权限的程序时请勿使用system(),system()会继承环境变量,通过环境变量可能会造成系统安全的问题。
范例
        #i nclude<stdlib.h>
main()
{
system(“ls -al /etc/passwd /etc/shadow”);
}
执行结果:

-rw-r--r-- 1 root root 705 Sep 3 13 :52 /etc/passwd
-r--------- 1 root root 572 Sep 2 15 :34 /etc/shado

例2:

char tmp[];
sprintf(tmp,"/bin/mount -t vfat %s /mnt/usb",dev);
system(tmp);
其中dev是/dev/sda1。


曾经的曾经,被system()函数折磨过,之所以这样,是因为对system()函数了解不够深入。只是简单的知道用这个函数执行一个系统命令,这远远不够,它的返回值、它所执行命令的返回值以及命令执行失败原因如何定位,这才是重点。当初因为这个函数风险较多,故抛弃不用,改用其他的方法。这里先不说我用了什么方法,这里必须要搞懂system()函数,因为还是有很多人用了system()函数,有时你不得不面对它。


先来看一下system()函数的简单介绍:
view source
print?
1#include <stdlib.h>
2int system(constchar *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.

  system()函数调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串command中读取命令;
  在该command执行期间,SIGCHLD是被阻塞的,好比在说:hi,内核,这会不要给我送SIGCHLD信号,等我忙完再说;
  在该command执行期间,SIGINT和SIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。

再来看一下system()函数返回值:
The value returned is -1 on error (e.g. fork(2) failed), and the return status of the command otherwise. This latter return status is in the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127).
If the value of command is NULL, system() returns nonzero if the shell is available, and zero if not.
为了更好的理解system()函数返回值,需要了解其执行过程,实际上system()函数执行了三步操作: 
1.fork一个子进程;
2.在子进程中调用exec函数去执行command;
3.在父进程中调用wait去等待子进程结束。
  对于fork失败,system()函数返回-1。
  如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值。
  (注意,command顺利执行不代表执行成功,比如command:"rm debuglog.txt",不管文件存不存在该command都顺利执行了)
  如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127.
如果command为NULL,则system()函数返回非0值,一般为1.

看一下system()函数的源码
  看完这些,我想肯定有人对system()函数返回值还是不清楚,看源码最清楚,下面给出一个system()函数的实现:
view source
print?
01int system(constchar * cmdstring)
02{
03    pid_t pid;
04    intstatus;
05 
06if(cmdstring == NULL)
07{
08    return(1); //如果cmdstring为空,返回非零值,一般为1
09}
10 
11if((pid = fork())<0)
12{
13    status = -1;//fork失败,返回-1
14}
15else if(pid == 0)
16{
17    execl("/bin/sh","sh""-c", cmdstring, (char*)0);
18    _exit(127);// exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~
19}
20else //父进程
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    returnstatus; //如果waitpid成功,则返回子进程的返回状态
33}

仔细看完这个system()函数的简单实现,那么该函数的返回值就清晰了吧,那么什么时候system()函数返回0呢?只在command命令返回0时。

看一下该怎么监控system()函数执行状态
  这里给我出的做法:
view source
print?
01int status;
02if(NULL == cmdstring)//如果cmdstring为空趁早闪退吧,尽管system()函数也能处理空指针
03{
04    returnXXX;
05}
06status = system(cmdstring);
07if(status < 0)
08{
09    printf("cmd: %s\t error: %s", cmdstring,strerror(errno));// 这里务必要把errno信息输出或记入Log
10    returnXXX;
11}
12 
13if(WIFEXITED(status))
14{
15    printf("normal termination, exit status = %d\n", WEXITSTATUS(status));//取得cmdstring执行结果
16}
17else if(WIFSIGNALED(status))
18{
19    printf("abnormal termination,signal number =%d\n", WTERMSIG(status));//如果cmdstring被信号中断,取得信号值
20}
21else 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”。

关于这个错误的分析,将会专门写一篇文章分析。

2012-04-14 任洪彩 qdurenhongcai@163.com

转载请注明出处。

 

#include<iostream> //标准输入输出流,不同于stdio.h和cstdio
#include<unistd.h> //unix标准头文件,win环境下没有
#include<cstring> //c字符
#include<string> //c++字符,不同于cstring和string.h两个头文件
#include<cstdlib> //c语言的stdlib.h,这个里面使用了其中的system

typedef char* charPtr; //定义一个字符指针,以定义动态数组
using namespace std; //标准命名空间

int main( int argc, char* argv[] )
{
  string a; //定义c++字符串a
  a = " /home/fruit/bin/dx 哈哈,我终于成功啦!!!"; //给a赋值,也就是我想要执行的命令。这个命令在我系统的实际意思是执行的写的飞信伪脚本(用perl写的),然后发送“哈哈,我终于成功啦!!!”这句话给我自己。
  int n; //定义变量n,目的是取a字符串长度
  n = a.length(); //n取值
  charPtr b; //定义字符指针b
  b = new char[n+1]; //为b分配内存空间,为a的长度加一,因为c字符要以\0结尾
  a.data(); //将a以c字符返回
  a.c_str(); //将a加上\0
  a.copy(b , n, 0); //将a复制给b,其中b是指针类型,n是复制长度,0是index位置
  cout << " a " << a << endl //以下三行很简单,就是输出一些东西,让我自己了解程序运行情况,可以删除的
  << " b " << b << endl
  << " n " << n << endl;
  system ( b ) ; //前面所有为它服务,用system调用
}
我的初衷是想要将一个string的字符串作为一个新的进程执行。由于system只能支持char字符,所以需要将c++的string字符串转换为c的char字符组。
然后实际上我们可以不使用system()函数,可以直接调用exec函数族。
exec函数族中是有execve是系统调用,其余的均是以execve为基础的函数调用。
对于exec函数族是包含在unistd.h头文件中的。具体的exec函数族我就不在这里详述了,下次有机会有时间在讲一讲。

 

system是用shell来调用程序 
而exec是直接让你的程序代替用来的程序运行 

看一下,下面的例子,因为这里是perl组所以就用perl来具例子,实际情况下,在C中也差不多.. 

--------------------------------- 
#!/usr/bin/perl 
#example1.pl 
system("you_program"); 
print "You can see me! "; 
--------------------------------- 

--------------------------------- 
#!/usr/bin/perl 
#example2.pl 
exec("you_program"); 
print "You can't see me! "; 
--------------------------------- 

在example1.pl中,在你的程序执行完毕以后,会执行print语句。 
在example2.pl中,由于exec将程序your_program代替了本身, 
因此程序不再会执行print语句。 

在Linux下,exec通常会和fork语句一起用。 

看下面的这个例子 
-------------------------------------------- 
#!/usr/bin/perl 
#example for fork() and exec() 
$pid = fork(); 
if ($pid < 0) { 
#fork函数出错 
print "ERROR "; 
exit(-1); 
} else if ($pid == 0) { 
#这里是子进程 
print "I'm son! "; 
#执行其它的程序 
exec("your_program"); 
} else { 
#这里是父进程 
print "i'm father! "; 
#等待子进程结束后返回 
wait(); 
exit(0); 

函数名: system  

功   能: 发出一个DOS命令  
用   法: int system(char *command);  
system函数已经被收录在标准c库中,可以直接调用

system()函数用于向操作系统传递控制台命令行,以WINDOWS系统为例,通过system()函数执行命令和在DOS窗口中执行命令的效果是一样的,所以只要在运行窗口中可以使用的命令都可以用SYSTEM()传递,但要注意的是输入斜线时要输入两个,以名C语言当作转义字符处理。


常用的DOS命令,可用system函数调用:

ASSOC    显示或修改文件扩展名关联。
AT       计划在计算机上运行的命令和程序。
ATTRIB   显示或更改文件属性。
BREAK    设置或清除扩展式 CTRL+C 检查。
CACLS    显示或修改文件的访问控制列表(ACLs)。
CALL     从另一个批处理程序调用这一个。
CD       显示当前目录的名称或将其更改。
CHCP     显示或设置活动代码页数。
CHDIR    显示当前目录的名称或将其更改。
CHKDSK   检查磁盘并显示状态报告。
CHKNTFS  显示或修改启动时间磁盘检查。
CLS      清除屏幕。
CMD      打开另一个 Windows 命令解释程序窗口。
COLOR    设置默认控制台前景和背景颜色。
COMP     比较两个或两套文件的内容。
COMPACT  显示或更改 NTFS 分区上文件的压缩。
CONVERT  将 FAT 卷转换成 NTFS。您不能转换
         当前驱动器。
COPY     将至少一个文件复制到另一个位置。
DATE     显示或设置日期。
DEL      删除至少一个文件。
DIR      显示一个目录中的文件和子目录。
DISKCOMP 比较两个软盘的内容。
DISKCOPY 将一个软盘的内容复制到另一个软盘。
DOSKEY   编辑命令行、调用 Windows 命令并创建宏。
ECHO     显示消息,或将命令回显打开或关上。
ENDLOCAL 结束批文件中环境更改的本地化。
ERASE    删除至少一个文件。
EXIT     退出 CMD.EXE 程序(命令解释程序)。
FC       比较两个或两套文件,并显示
         不同处。
FIND     在文件中搜索文字字符串。
FINDSTR  在文件中搜索字符串。
FOR      为一套文件中的每个文件运行一个指定的命令
FORMAT   格式化磁盘,以便跟 Windows 使用。
FTYPE    显示或修改用于文件扩展名关联的文件类型。
GOTO     将 Windows 命令解释程序指向批处理程序
         中某个标明的行。
GRAFTABL 启用 Windows 来以图像模式显示
         扩展字符集。
HELP     提供 Windows 命令的帮助信息。
IF       执行批处理程序中的条件性处理。
LABEL    创建、更改或删除磁盘的卷标。
MD       创建目录。
MKDIR    创建目录。
MODE     配置系统设备。
MORE     一次显示一个结果屏幕。
MOVE     将文件从一个目录移到另一个目录。
PATH     显示或设置可执行文件的搜索路径。
PAUSE    暂停批文件的处理并显示消息。
POPD     还原 PUSHD 保存的当前目录的上一个值。
PRINT    打印文本文件。
PROMPT   更改 Windows 命令提示符。
PUSHD    保存当前目录,然后对其进行更改。
RD       删除目录。
RECOVER  从有问题的磁盘恢复可读信息。
REM      记录批文件或 CONFIG.SYS 中的注释。
REN      重命名文件。
RENAME   重命名文件。
REPLACE  替换文件。
RMDIR    删除目录。
SET      显示、设置或删除 Windows 环境变量。
SETLOCAL 开始批文件中环境更改的本地化。
SHIFT    更换批文件中可替换参数的位置。
SORT     对输入进行分类。
START    启动另一个窗口来运行指定的程序或命令。
SUBST    将路径跟一个驱动器号关联。
TIME     显示或设置系统时间。
TITLE    设置 CMD.EXE 会话的窗口标题。
TREE     以图形模式显示驱动器或路径的目录结构。
TYPE     显示文本文件的内容。
VER      显示 Windows 版本。
VERIFY   告诉 Windows 是否验证文件是否已正确
         写入磁盘。
VOL      显示磁盘卷标和序列号。
XCOPY    复制文件和目录树。

 

system这个函数是系统调用。类似于再cmd窗口中执行,其参数是可执行的命令,如  cls  ,notepad.exe。

 

eg:

#include <process.h>

//or #include<stdlib.h>

void main(){

     system("ping 10.10.10.11 -t");

     system("start /b ping 10.10.10.11 -t");

}


ref:

http://blog.csdn.net/pipisorry/article/details/33024727

http://wenku.baidu.com/view/cfc565d5195f312b3169a5d8.html

http://www.jb51.net/article/42281.htm



0 0
原创粉丝点击