C++ code中调用perl/shll脚本方法

来源:互联网 发布:java多人聊天室源代码 编辑:程序博客网 时间:2024/05/21 01:45

C++里,如果想要执行某个脚本或者命令,有三种方法:

一、调用系统函数system().  

intsystem(const char *string),所在头文件:#include<stdlib.h>

函数说明system()会调用fork()产生子进程,由子进程来调用/bin/sh-c  string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD信号会被暂时搁置,SIGINTSIGQUIT信号则会被忽略。

参数说明:string,可以为对应的shellperl脚本的字符串。

返回值:=-1:出现错误|=0:调用成功但是没有出现子进程|>0:成功退出的子进程的id

如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值。如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为 system()调用/bin/sh失败所返回的127,因此最好能再检查errno来确认执行成功。

注:在编写具有SUID/SGID权限的程序时请勿使用system()system()会继承环境变量,通过环境变量可能会造成系统安全的问题,



EG1:if(system(NULL)) puts("system execute OK!");

  else exit(-1);

  int errcode =system(cmd.c_str());

  if (errno ==EAGAIN)

   INFO(NBI_STR("the error is EAGAIN."));

  else if (errno== EINTR)

   INFO(NBI_STR("the error is EINTR"));

  else if (errno== ENOMEM)

   INFO(NBI_STR("the error is ENOMEM"));

  elseINFO(NBI_STR("the error is undefined"));

 

EG2:#include<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/shadow



 

 

二、调用创建子进程函数fork()  并执行execl()


example:

pid_tpid = fork();

  if(pid < 0)

Cout<<"forkfailed! errno :"<< errno<<endl;

  else if(pid == 0)

  { cout<<"fork success "<<endl;

if(execl("/bin/sh","sh", "-c", “pwd”, NULL) < 0)

    {

Cout<<”executefail,errno:"<<errno<<endl;

     exit(-1);

    }

  }

  int termstat;

  while(waitpid(pid,&termstat,0) != pid)

  {

    if(errno !=EINTR)

    {

Cout<<”waitpidfailed!errno"<<errno<<endl;

      break;

    }

   }


 

这里主要有三个函数fork()/execl()/waitpid().

pid_t fork( void):                    所在头文件:#include<unistd.h>

函数说明:该函数在现有进程中创建一个新的进程,即子进程,它被调用一次,但返回两次。即子进程中返回值为0,父进程中返回子进程ID.子进程是父进程的副本,将获得父进程的数据空间、堆、栈等资源的副本。即父子进程之间不共享这些存储空间。不同的系统下,父子进程的运行顺序是不相同的。故而在代码中不能对此作出任何假设。故而我们可以通过该函数的返回值来判定是父进程还是子进程,并在父子进程中分别实现不同的动作。在该例子中,子进程调用execl函数执行相应的shell脚本,父进程调用waitpid()等待子进程执行完。当父进程稍后的处理内容有赖于子进程执行完后的结果,需要调用waitpid等待子进程完成。

返回值:在子进程中返回0,在父进程中返回子进程的id.

错误信息:创建子进程出错时有如下错误信息:

EAGAIN:达到进程数上限

ENOMEM:没有足够空间给一个新进程分配。

注:pid_t是一个宏定义,其实质是int,被定义在#include<sys/types.h>中。

intexecl(const char *path, const char *arg[0], *arg[1] ...,NULL)所在头文件:#include<unistd.h>

函数说明:该函数是exec函数族中的一个,另有函数:execle,execlp, execv, execve, execvp。该族函数主要作用为,执行参数中的命令或者是其他程序、脚本,函数名里含l(list)表示参数列表,v

参数说明:path-指向要执行的文件路径. argv[0],argv[1]...-执行该文件时传递的参数列表,最后一个参数须用空指针NULL/(char*)0作结束。

返回值:执行成功则返回不返回值,失败则返回-1,失败原因存于errno中,可通过perror打印出对应的错误信息。

Example:#include <unistd.h>

int main()

{

// 执行/bin目录下的ls, 第一参数为程序名ls,第二个参数为"-al", 第三个参数"/etc/"

Int state = execl("/bin/ls","ls","-al","/etc/",NULL);

If(state<0)

{perror(“execute fail!”);   return -1;}

return 0;

}

注:这里如果命令:ls –al /etc/执行不成功,那么将报错(executefail: $errno),即perror()输出的内容为其参数内容再加上全局变量errno中对应的内容。

pid_twaitpid(pid_t pid,int * status,int options)                 所在的头文件:#include<sys/wait.h>

函数说明:该函数可暂时停止目前进程的执行,直到有信号来到或子进程。

参数说明:status-子进程的结束状态值。如果不在意结束状态值,则参数 status 可设为 NULL。

pid -欲等待的子进程识别码.其对应的含义如下:

pid<-1等待进程组识别码为 pid绝对值的任何子进程。

pid=-1 等待任何子进程,相当于 wait()

pid=0 等待进程组识别码与目前进程相同的任何子进程。

pid>0 等待任何子进程识别码为 pid的子进程。

Options-控制waitpid()的一些额外选项。可以为 0或用"|"运算符联接起来的选项。选项包括:

 

0:若调用该函数时pid指定的子进程没有结束,则等待,直到子进程结束,返回子进程的pid.

WNOHANG:若子进程没有结束,则不等待,而是直接返回0,若结束,则返回子进程的ID

WUNTRACED:若子进程进入暂停(STOP)状态,则马上返回,但子进程的结束状态不予以理会。

WCONTINUED:若子进程进入继续(CONTINUE)状态,函数马上返回,父进程被唤醒。

返回值:子进程的ID0或者是负数,代表pid不对。

注:子进程的结束状态将置于status中,一般子进程退出的方式有:1)通过exit()退出。2)信号导致退出,如段错误信号SIGSEGV、非法指令信号SIGILL、管道断裂信号SIGPIPE、程序流产信号SIGABORT,对于这些退出原因,父进程需要知道其原因。以上两种退出方式均会重置status状态。另外还有一种也会置status状态的情况,即:子进程的状态发生变化时,一般为进程转换为STOPPED或者CONTINUED状态时,默认状态下父进程会收到这些状态信号,但不会在这两个状态下被唤醒。只有设置了waitpid()中参数option的值时才有可能唤醒父进程。若父进程想屏蔽掉这两个信号,不接收它们,那么只需要在注册SIGCHLD时设置SA_NOCLDSTOP,来拒绝进程变化时接收信号的行为。系统中对status的状态作了一些宏定义,具体有:

WIFEXITED(status)若子进程正常结束时,为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit_eixt的低8

WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。

WIFSIGNALED(status)若子进程因信号而异常结束,则为真;对于这种情况可执行WTERMSIG(status),取得子进程结束的信号编号。

WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED来判断后才使用此宏。

WIFSTOPPED(status) 若子进程当前为暂停状态时,则该量为真;对于这种情况可执行WSTOPSIG(status),取得子进程暂停的信号编号。

WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。

 

 

 

三、             利用封装的popen()


Examplechar line[MAXLINE];

memset(line, '\0', MAXLINE);

FILE *fpout;


if ((fpout = popen(cmd.c_str(), "r")) !=NULL)

{Cout<<”command execution!!! "<<endl;

while(fgets(line, 233, fpout) != NULL)

 {Cout<<line<<endl; }

   int res =pclose(fpout);

   if (res== -1) {

switch(errno)

{case EMFILE: cout<<"the error is:EMFILE."<<endl;

case ECHILD: cout<<”the error is ECHILD."<<endl;

case EINVAL:  cout<<”theparameter type is not legal."<<endl;

default: cout<<errno<<endl;}

 }

   else {cout<<”popenfail”<<endl;

    return -1;}



这里主要有三个函数popen()/fgets()/pclose()

FILE* popen(constchar*command,constchar*type)

int      pclose (FILE *stream );

所在头文件#include<stdio.h>

 

函数说明popen()函数通过创建一个管道,调用fork 产生一个子进程,执行一个 shell以运行命令来开启一个进程. 此进程必须由pclose()函数关闭。该函数类似于封装了第二种方法。

pclose() 函数关闭标准 I/O流,等待命令执行结束,然后返回 shell 的终止状态。如果 shell不能被执行,则pclose() 返回的终止状态与 shell已执行 exit 一样。

参数说明:

command:指向以 NULL结束的 shell 命令字符串的指针,这行命令将被传到 bin/sh 并使用-c标志,shell 将执行这个命令。Type:(“r”)或者写(“w”)中的一种.若为“r”则返回的文件指针连接到执行command后的标准输出。若为“w”则返回的文件指针连接到command的标准输入。stream:popen()创建的file stream.

返回值:popen()若调用fork()或pipe()失败,或不能分配内存,则将返回NULL。否则返回标准I/O流。

       pclose():返回系统调用wait4()的状态。若stream无效或系统调用wait4()失败,则返回-1.

返回错误类型:若分配内存失败,则设置errno;若调用fork()或pipe()失败,则errno将被置为与这两个函数调用失败时相应的错误类型;若参数type不合法,errno将返回EINVAL。

char*fgets(char *buf, int bufsize, FILE *stream)    所在头文件:

函数说明:该函数从文件结构体指针stream中读取数据,每次读取一行,读取的数据保存在buf指向的字符数组中,每次最多读取bufsize-1个字符,如果文件中的该行,不足bufsize个字符,则读完该行就结束。函数成功将返回buf,失败或读到文件结尾返回NULL。因读到结尾时也返回NULL,故而不可通过返回值判断函数执行是否成功。而应借助feof()ferror()来判断。

参数说明 *buf: 字符型指针,指向用来存储所得数据的地址。

                    bufsize:整型数据,指明buf指向的字符数组的大小。

                  *stream:文件结构体指针,将要读取的文件流。

返回值:bufsize< =0,则返回NULL,bufsize ==1,则返回“”,即一个空串。若成功,则返回string,即字符串的首地址。若出错,或读到结尾,则返回NULL

0 0
原创粉丝点击