linux下如何用c语言调用shell命令

来源:互联网 发布:常用的数据挖掘模型 编辑:程序博客网 时间:2024/05/29 21:29
C程序调用shell脚本共有三种法子 :system()、popen()、exec系列函数 system()不用你自己去产生进程,它已经封装了,直接加入自己的命令exec 需要你自己 fork 进程,然后exec 自己的命令。

popen() 也可以实现执行你的命令,比system 开销小

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来确认履行 成功 。

system命令以其简略 高效的作用得到很很广泛 的利用 ,下面是一个例子

例:在~/test/目录下有shell脚本test.sh,内容为

#!bin/bash

#test.sh

echo hello

在同层目录下新建一个c文件system_test.c,内容为:

#include<stdlib.h>

int main()

{

system("~/test/test.sh");//system可以调用脚本也可以直接调用linux命令,和shell输入基本一致。

}

履行 效果 如下:

[root@localhost test]$gcc system_test.c -o system_test

[root@localhost test]$./system_test

hello

[root@localhost test]$

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

popen()会调用fork()产生 子历程,然后从子历程中调用/bin/sh -c来履行参数command的指令。参数type可应用 “r”代表读取,“w”代表写入。遵循此type值,popen()会建立管道连到子历程的标准 输出设备 或标准 输入设备 ,然后返回一个文件指针。随后历程便可利用 此文件指针来读取子历程的输出设备或是写入到子历程的标准 输入设备 中。此外,所有应用 文 件指针(FILE*)操作的函数也都可以应用,除了fclose()以外。

返回值:若成功 则返回文件指针,否则返回NULL,差错原因存于errno中。注意:在编写具SUID/SGID权限的程序时请尽量避免应用popen(),popen()会继承环境变量,通过环境变量可能会造成系统安全的问题。

例:C程序popentest.c内容如下:

#include<stdio.h>

main

{

FILE * fp;

charbuffer[80];

fp=popen(“~/myprogram/test.sh”,”r”);

fgets(buffer,sizeof(buffer),fp);

printf(“%s”,buffer);//此时的buffer内容已经重新生成适合管道操作的数据

pclose(fp);

}

履行 效果 如下:

[root@localhost test]$ vim popentest.c

[root@localhost test]$ gcc popentest.c -o popentest

[root@localhost test]$ ./popentest

/root/test

[root@localhost test]$


3.exec


exec函数族包括6个函数:

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, const char *envp[]);
int execv(const char *path, const char *argv[]);
int execve(const char *path, const char *argv[], const char *envp[];
int execvp(const char *file, const char *argv[]);


参数说明:

execl的第一个参数是包括路径的可执行文件,后面是列表参数,列表的第一个为命令path,接着为参数列表,最后必须以NULL结束。
execlp的第一个参数可以使用相对路径或者绝对路径。
execle,最后包括指向一个自定义环境变量列表的指针,此列表必须以NULL结束。
execv,v表示path后面接收的是一个向量,即指向一个参数列表的指针,注意这个列表的最后一项必须为NULL。
execve,path后面接收一个参数列表向量,并可以指定一个环境变量列表向量。
execvp,第一个参数可以使用相对路径或者绝对路径,v表示后面接收一个参数列表向量。

    exec被调用时会替换调用它的进程的代码段和数据段(但是文件描述符不变),直接返回到调用它的进程的父进程,如果出错,返回-1并设置errno。


例子:
#include <unistd.h>
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");
        }
}

    程序里调用了2 个Linux 常用的系统命令,echo和env。echo会把后面跟的命令行参数原封不动的打印出来,env用来列出所有环境变量。
    由于各个子进程执行的顺序无法控制,所以有可能出现一个比较混乱的输出--各子进程打印的结果交杂在一起,而不是严格按照程序中列出的次序。
最常见的错误:
    平时的编程中,如果用到了exec 函数族,一定记得要加错误判断语句。因为与其他系统调用比起来,exec很容易受伤,被执行文件的位置,权限等很多因素都能导致该调用的失败。
    最常见的错误是:
    1)找不到文件或路径,此时errno 被设置为ENOENT;
    2)数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT;
    3)没有对要执行文件的运行权限,此时errno被设置为EACCES。

0 0
原创粉丝点击