[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.

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.

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

问题

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

转自: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;
        }

}

执行结果如下:
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等文件操作函数去读取输出结果。

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

例如

#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");
    }
}

 

转载于:https://www.cnblogs.com/aaronLinux/p/6902952.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值