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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM