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[];
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[];
memset(buff,,sizeof(buff));
if(NULL==(fstream=popen("ls -l","r")))
{
fprintf(stderr,"execute command failed: %s",strerror(errno));
return -;
}
if(NULL!=fgets(buff, sizeof(buff), fstream))
{
printf("%s",buff);
}
else
{
pclose(fstream);
return -;
}
pclose(fstream);
return ;
}

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()==) {
if(execl("/bin/echo", "echo", "executed by execl", NULL)<)
perror("Err on execl");
}
if(fork()==) {
if(execlp("echo", "echo", "executed by execlp", NULL)<)
perror("Err on execlp");
}
if(fork()==) {
if(execle("/usr/bin/env", "env", NULL, envp)<)
perror("Err on execle");
}
if(fork()==) {
if(execv("/bin/echo", argv_execv)<)
perror("Err on execv");
}
if(fork()==) {
if(execvp("echo", argv_execvp)<)
perror("Err on execvp");
}
if(fork()==) {
if(execve("/usr/bin/env", argv_execve, envp)<)
perror("Err on execve");
}
}

[shell]C语言调用shell脚本接口的更多相关文章

  1. linux下怎样用c语言调用shell命令

    C程序调用shell脚本共同拥有三种法子 :system().popen().exec系列数call_exec1.c , system() 不用你自己去产生进程.它已经封装了,直接增加自己的命令 ex ...

  2. C 语言调用python 脚本函数

    刚好几个月前做过,C++ 函数里面先加载python 脚本,再调用 里面的 def 函数,我把代码贴出来,你在main 函数里面,调用getDataByScript 函数,另外相同目录下放一个 fuc ...

  3. perl 调用shell脚本

    perl调用shell命令 perl调用shell shell调用perl Perl执行shell命令的几种方式及其区别

  4. 在C++代码中调用L脚本语言

    L脚本语言同意被其他编程语言调用.如C++,非常easy.仅仅要在宿主语言中载入L脚本引擎的动态库 直接调用这两个函数就能够了 extern "C" int __stdcall S ...

  5. 脚本、脚本语言、shell脚本

    脚本是批处理文件的延伸,是一种纯文本保存的程序,一般来说的计算机脚本程序是确定的一系列控制计算机进行运算操作动作的组合,在其中可以实现一定的逻辑分支等.脚本程序相对一般程序开发来说比较接近自然语言,可 ...

  6. Spring Boot 实现看门狗功能 (调用 Shell 脚本)

    需要实现看门狗功能,定时检测另外一个程序是否在运行,使用 crontab 仅可以实现检测程序是否正在运行,无法做到扩展,如:手动重启.程序升级(如果只需要实现自动升级功能可以使用 inotify)等功 ...

  7. 【原】Gradle调用shell脚本和python脚本并传参

    最近由于项目自动化构建的需要,研究了下gradle调用脚本并传参的用法,在此作个总结. Pre build.gradle中定义了$jenkinsJobName $jenkinsBuild两个Jenki ...

  8. 调用shell脚本,IP处理

    //调用shell脚本,IP处理 package com.letv.sdns.web.utils; import org.slf4j.Logger; import org.slf4j.LoggerFa ...

  9. C程序调用shell脚本共有三种方法

    C程序调用shell脚本共有三种法子 :system().popen().exec系列函数call_exec1.c ,内容为:system() 不用你自己去产生进程,它已经封装了,直接加入自己的命令e ...

随机推荐

  1. Linux0.11内核剖析--内核代码(kernel)--sched.c

    1.概述 linux/kernel/目录下共包括 10 个 C 语言文件和 2 个汇编语言文件以及一个 kernel 下编译文件的管理配置文件 Makefile.其中三个子目录中代码注释的将放在后面的 ...

  2. JS-为句柄添加监听函数

    具体谈如何实现JS为句柄添加监听函数之前先看一段代码,算是抛出这个问题. <html> <head> <title>JS为句柄添加监听函数</title> ...

  3. java中构建同时兼容linux和windows程序时遇到的文件路径分割符问题解决方案

    最近在做一个自动上传文件的客户端,因为 file.getAbsolutePath()  在Mac和linux下的分割符是“/”,而在windows操作系统下的分割符则是“\”,我们程序中固然可以通过调 ...

  4. public类型中internal成员

    今天遇到一问题,找到下面的两篇文章,研究比较深入,特转了一下, 最近除了搞ASP.NET MVC之外,我也在思考一些编程实践方面的问题.昨天在回家路上,我忽然对一个问题产生了较为清晰的认识.或者说,原 ...

  5. ganglia组播和单播

    ganglia快速开始向导(翻译自官方wiki) 发布于 2012 年 1 月 23 日 由 admin 2 comments发表评论   转自:http://cryinstall.com/?p=18 ...

  6. 使用HTML5的两个api,前端js完成图片压缩

    主要用了两个html5的 API,一个file,一个canvas,压缩主要使用cnavas做的,file是读取文件,之后把压缩好的照片放入内存,最后内存转入表单下img.src,随着表单提交. 照片是 ...

  7. 关于angularjs中路由页面强制更新的问题

    有这么一个问题,在页面内路由页面跳转时,第一次跳入路由页面时是正常的,但是第二次会记住第一次时的状态,有时候并不想这样,想强制更新这个路由页面. 有一种方式就是使用 ui-sref-opts功能,我试 ...

  8. request.getServletContext()

    servlect 3.0 支持,低版本不支持,报错的话看jar包的引用.

  9. Unity Editor类常用方法

    http://www.cnblogs.com/zhaoqingqing/p/3944718.html 一些比较常用的Editor功能我在之前的博客中也有提到过所以就不详细写啦,今天参考了一下 麒麟子( ...

  10. Python list删除元素

    pop()方法 pop(n) 从list删除元素Paul同学刚来几天又要转走了,那么我们怎么把Paul 从现有的list中删除呢?如果Paul同学排在最后一个,我们可以用list的pop()方法删除: ...