[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");
    }
}
相關文章
相關標籤/搜索