[shell]C语言调用shell脚本接口

Use popen if you want to run a shell command and want the parent process to be able to talk to the child. (It hooks the child's input or output up to the stream you get back.) Otherwise, prefer the execfamily of functions (likely in conjunction with fork); exec'd processes inherit most open file descriptors (including stdin, stdout, and stderr), so you can hook input and output up to whatever you like...plus, there are fewer security implications.linux

system is generally best avoided unless you have to run shell commands. It spawns a shell to run the command, and the shell can parse the command any way it likes. In particular, certain environment variables (like $IFS and/or $PATH) can be modified in such a way as to cause the parent to execute programs you never intended it to. Although popen appears to do the same thing, it at least provides functionality that makes it worthwhile in the general case.shell

---------------------------------------------------------------------------------------------------------------------------------------------------------编程

问题ubuntu

1. system能够直接运行带参数的shell脚本,特别是数字参数如,system("/bin/bash ~/test.sh 100 10"); 那么execlp怎么作呢vim

转自:http://blog.csdn.net/luokehua789789/article/details/53117904数组

-------------------------------------------------------------------------------------------------------------------------------

1)system(shell命令或shell脚本路径);

    执行过程:system()会调用 fork()产生子进程,由子进程来调用/bin/sh -c string来执行参数string字符串所表明的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
    返回值:若是system()在调用/bin/sh时失败则返回127,其余失败缘由返回-1。若参数string为空指针(NULL),则返回非零值。若是 system() 调用成功则最后会返回执行shell命令后的返回值,可是此返回值也有可能为system()调用/bin/sh失败所返回的127, 所以最好能再检查errno 来确认执行成功
    注意:在编写具备SUID/SGID权限的程序时 尽可能避免使用system(),system()会继承环境变量,经过环境变量可能会形成 系统安全的问题。
例:在~/myprogram/目录下有shell脚本test.sh,内容为
#!bin/bash
#test.sh
echo $HOME
在该目录下新建一个c文件systemtest.c,内容为:
#include
/*This program is used to test function system*/
void main()
{
      int rv = system("~/myprogram/test.sh");

        if (WIFEXITED(rv))
        {
             printf("subprocess exited, exit code: %d\n", WEXITSTATUS(rv));
             if (0 == WEXITSTATUS(rv))
             {
                  // if command returning 0 means succeed
                  printf("command succeed");
             }
             else
             {
                  if(127 == WEXITSTATUS(rv))
                  {
                       printf("command not found\n");
                       return WEXITSTATUS(rv);
                  }
                  else
                  {
                       printf("command failed: %d\n", strerror(WEXITSTATUS(rv)));
                       return WEXITSTATUS(rv);
                  }
             }
         }
        else
        {
             printf("subprocess exit failed");
             return -1;
        }安全

}bash

执行结果以下:
xiakeyou@ubuntu:~/myprogram$ gcc systemtest.c -o systemtest
xiakeyou@ubuntu:~/myprogram$ ./systemtest 
/home/d/e/xiakeyou
xiakeyou@ubuntu:~/myprogram$

2)popen(char *command,char *type)    

    执行过程:popen()会调用 fork()产生子进程,而后从子进程中调用/bin/sh -c来执行参数command的指令。参数type可以使用“r”表明读取,“w”表明写入。依照此type值,popen()会创建管道连到子进程的标准输出设备或标准输入设备,而后返回一个文件指针。随后进程即可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。此外,全部使用文件指针(FILE*)操做的函数也均可以使用,除了fclose()之外。
    返回值:若成功则返回文件指针,不然返回NULL,错误缘由存于errno中。
 另外可使用pclose捕捉该条命令有没有正确被执行,只是pclose不能在popen后面太快不执行,不然可能pipe error.
However,  pclose(3) returns the exit value of the process that you were running. Non-zero exit processes are typical of programs to signal error conditions on exit, and some programs are even nice enough to signal to you what the error was based on the exit value.
    注意:在编写具SUID/SGID权限的程序时请 尽可能避免使用popen(),popen()会继承环境变量,经过环境变量可能会形成系统安全的问题。
例:C程序popentest.c内容以下:
#include
main()
{
     FILE * fp;
     charbuffer[80];
     fp=popen(“~/myprogram/test.sh”,”r”);
     fgets(buffer,sizeof(buffer),fp);
     printf(“%s”,buffer);
     pclose(fp);
}
执行结果以下:
xiakeyou@ubuntu:~/myprogram$ vim popentest.c
xiakeyou@ubuntu:~/myprogram$ gcc popentest.c -o popentest
xiakeyou@ubuntu:~/myprogram$ ./popentest
/home/d/e/xiakeyou
xiakeyou@ubuntu:~/myprogram$ 

popen函数执行命令后,返回一个指向该命令输出的文件句柄,接下来就能够用fgets等文件操做函数去读取输出结果。app

#include   
FILE *popen(const char *command, const char *type); 
int pclose(FILE *stream);  
type的参数只能是“r”或"w"

例如less

#include  
#include  
int main(int argc,char*argv[]){  
    FILE *fstream=NULL;    
    char buff[1024];  
    memset(buff,0,sizeof(buff));  
    if(NULL==(fstream=popen("ls -l","r")))    
    {   
        fprintf(stderr,"execute command failed: %s",strerror(errno));    
        return -1;    
    }   
    if(NULL!=fgets(buff, sizeof(buff), fstream))   
    {   
        printf("%s",buff);  
    }   
    else  
    {  
        pclose(fstream);  
        return -1;  
    }  
    pclose(fstream);  
    return 0;   
}

3)linux exec的用法

说是exec系统调用,实际上在Linux中,并不存在一个exec()的函数形式,exec指的是 一组函数,一共有6个,分别是:
#include 
extern char **environ;
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[]);
int execve(const char *path, char *const argv[], char *const envp[]);
其中只有execve是真正意义上的系统调用,其它都是在此基础上通过包装的库函数。
exec函数族的做用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。 这里的可执行文件既能够是二进制文件,也能够是任何Linux下可执行的脚本文件
与通常状况不一样,exec函数族的函数 执行 成功后不会返回,由于调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,很有些神似"三十六计"中的"金蝉脱壳"。看上去仍是旧的躯壳,却已经注入了新的灵魂。只有 调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。
如今咱们应该明白了,Linux下是如何执行新程序的,每当有进程认为本身不能为系统和拥护作出任何贡献了,他就能够发挥最后一点余热,调用任何一个exec,让本身以新的面貌重生;或者,更广泛的状况是,若是一个进程想执行另外一个程序,它就能够fork出一个新进程,而后调用任何一个exec,这样看起来就好像经过执行应用程序而产生了一个新进程同样。
事实上第二种状况被应用得如此广泛,以致于Linux专门为其做了优化,咱们已经知道,fork会将调用进程的全部内容原封不动的拷贝到新产生的子进程中去,这些拷贝的动做很消耗时间,而若是fork完以后咱们立刻就调用exec,这些辛辛苦苦拷贝来的东西又会被马上抹掉,这看起来很是不划算,因而人们设计了一种"写时拷贝(copy-on-write)"技术,使得fork结束后并不马上复制父进程的内容,而是到了真正实用的时候才复制,这样若是下一条语句是exec,它就不会白白做无用功了,也就提升了效率。

返回值
若是执行成功则函数不会返回,执行失败则直接返回-1,失败缘由存于errno 中。
你们在平时的编程中,若是用到了exec函数族,必定记得要加错误判断语句。由于与其余系统调用比起来,exec很容易受伤,被执行文件的位置,权限等不少因素都能致使该调用的失败。最多见的错误是:
  • 找不到文件或路径,此时errno被设置为ENOENT; 
  • 数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT; 
  • 没有对要执行文件的运行权限,此时errno被设置为EACCES。

函数族的意义

  • l 表示以参数列表的形式调用
  • v 表示以参数数组的方式调用
  • e 表示可传递环境变量
  • p 表示PATH中搜索执行的文件,若是给出的不是绝对路径就会去PATH搜索相应名字的文件,如PATH没有设置,则会默认在/bin,/usr/bin下搜索。
另: 调用时参数必须以NULL结束。原进程打开的文件描述符是不会在exec中关闭的,除非用fcntl设置它们的“执行时关闭标志(close on exec)”而原进程打开的目录流都将在新进程中关闭。
例子:
#include 
int main(int argc, char *argv[])
{
    char *envp[]={"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};
    char *argv_execv[]={"echo", "excuted by execv", NULL};
    char *argv_execvp[]={"echo", "executed by execvp", NULL};
    char *argv_execve[]={"env", NULL};
    if(fork()==0) {
        if(execl("/bin/echo", "echo", "executed by execl", NULL)<0)
        perror("Err on execl");
    }
    if(fork()==0) {
        if(execlp("echo", "echo", "executed by execlp", NULL)<0)
        perror("Err on execlp");
    }
    if(fork()==0) {
        if(execle("/usr/bin/env", "env", NULL, envp)<0)
        perror("Err on execle");
    }
    if(fork()==0) {
        if(execv("/bin/echo", argv_execv)<0)
        perror("Err on execv");
    }
    if(fork()==0) {
        if(execvp("echo", argv_execvp)<0)
        perror("Err on execvp");
    }
    if(fork()==0) {
        if(execve("/usr/bin/env", argv_execve, envp)<0)
            perror("Err on execve");
    }
}
相关文章
相关标签/搜索