linux 調用shell命令


C程序調用shell腳本共同擁有三種法子 :system()、popen()、exec系列數call_exec1.c ,

system() 不用你自己去產生進程。它已經封裝了,直接增加自己的命令
exec 須要你自己 fork 進程,然后exec 自己的命令

popen() 也能夠實現運行你的命令,比system 開銷小

 

方法一、system()的使用。我直接上代碼吧

 

int system(const char *command);

 

 

我在/home/book/shell新建一個test.sh文件例如以下:

 

<span style="font-size:18px;"><span style="font-size:18px;">#!bin/bash
echo $HOME
echo "the is test!"</span></span>


test.c文件例如以下:

 

 

<span style="font-size:18px;"><span style="font-size:18px;">#include<stdlib.h>
  
int   main()
{
  system("bash /home/book/shell/test.sh"); /* chmod +x test.sh ,路徑前面要加上bash */
  return 0;  
}</span></span>

 

運行例如以下命令來編譯:

<span style="font-size:18px;">gcc test.c -o test
</span>

 

測試命令:

 

<span style="font-size:18px;">./test</span>


結果例如以下:

 

 

<span style="font-size:18px;">/root
the is test!</span>

 

 

 

方法二:popen() 會調用fork()產生 子歷程,然后從子歷程中調用/bin/sh -c來履行 參數command的指令。參數type可應用 “r”代表讀取。“w”代表寫入。遵循此type值。popen()會建立 管道連到子歷程的標准 輸出設備 或標准 輸入設備 ,然后返回一個文件指針。

隨后歷程便可利用 此文件指針來讀取子歷程的輸出設備 或是寫入到子歷程的標准 輸入設備 中。此外,全部應用 文 件指針(FILE*)操作的函數也都能夠應用 ,除了fclose()以外。
 
    返回值:若成功 則返回文件指針,否則返回NULL,差錯 原因存於errno中。注意:在編寫具SUID/SGID權限的程序時請盡量避免應用 popen()。popen()會繼承環境變量。通過環境變量可能會造成系統安全的問題

 

FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);


其它不用改變我們直接改動test.c文件:

 

 

#include<stdio.h>
int  main()
{
    char buffer[80];
     FILE  *fp=popen("bash /home/book/shell/test.sh","r");
     fgets(buffer,sizeof(buffer),fp);
     printf("%s",buffer);
     pclose(fp);
return 0;
}

 

 


方法三:exec函數簇  (我不太懂,copy別人的。也沒有驗證。習慣方法一)

須要注意的是exec並非1個函數, 事實上它僅僅是一組函數的統稱, 它包含以下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, ..., 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[]; 

 

 

能夠見到這6個函數名字不同, 並且他們用於接受的參數也不同.

       實際上他們的功能都是幾乎相同的, 由於要用於接受不同的參數所以要用不同的名字區分它們, 畢竟c語言沒有函數重載的功能嘛..  

 

       可是實際上它們的命名是有規律的:

       exec[l or v][p][e]

       exec函數里的參數能夠分成3個部分,      運行文件部分,     命令參數部分,   環境變量部分.

        比如我要運行1個命令   ls -l /home/gateman  

        運行文件部分就是  "/usr/bin/ls"

        命令參賽部分就是 "ls","-l","/home/gateman",NULL              見到是以ls開頭 每1個空格都必須分開成2個部分, 並且以NULL結尾的啊.

        環境變量部分, 這是1個數組,最后的元素必須是NULL 比如  char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};

        

        好了說下命名規則:

        e興許,  參數必須帶環境變量部分,   環境變零部分參數會成為運行exec函數期間的環境變量, 比較少用

        l 興許,   命令參數部分必須以"," 相隔, 最后1個命令參數必須是NULL

        v 興許,   命令參數部分必須是1個以NULL結尾的字符串指針數組的頭部指針.         比如char * pstr就是1個字符串的指針, char * pstr[] 就是數組了, 分別指向各個字符串.

        p興許,   運行文件部分能夠不帶路徑, exec函數會在$PATH中找

 

          

         還有1個注意的是, exec函數會代替運行它的進程,  也就是說, 一旦exec函數運行成功, 它就不會返回了, 進程結束.   可是假設exec函數運行失敗, 它會返回失敗的信息,  並且進程繼續運行后面的代碼!

 

 

       通常exec會放在fork() 函數的子進程部分, 來替代子進程運行啦, 運行成功后子程序就會消失,  可是運行失敗的話, 必須用exit()函數來讓子進程退出!

       以下是各個樣例:

2.1  execv 函數

 

[cpp]  view plain  copy 
 
 在CODE上查看代碼片派生到我的代碼片
  1. int childpid;  
  2. int i;  
  3.   
  4. if (fork() == 0){  
  5.     //child process  
  6.     char * execv_str[] = {"echo", "executed by execv",NULL};  
  7.     if (execv("/usr/bin/echo",execv_str) <0 ){  
  8.         perror("error on exec");  
  9.         exit(0);  
  10.     }  
  11. }else{  
  12.     //parent process  
  13.     wait(&childpid);  
  14.     printf("execv done\n\n");  
  15. }  

注意字符串指針數組的定義和賦值

 

 

2.2  execvp 函數

 

 

[cpp]  view plain  copy 
 
 在CODE上查看代碼片派生到我的代碼片
  1. if (fork() == 0){  
  2.     //child process  
  3.     char * execvp_str[] = {"echo", "executed by execvp",">>", "~/abc.txt",NULL};  
  4.     if (execvp("echo",execvp_str) <0 ){  
  5.         perror("error on exec");  
  6.         exit(0);  
  7.     }  
  8. }else{  
  9.     //parent process  
  10.     wait(&childpid);  
  11.     printf("execvp done\n\n");  
  12. }  

 

 

2.3 execve 函數

 

[cpp]  view plain  copy 
 
 在CODE上查看代碼片派生到我的代碼片
  1. if (fork() == 0){  
  2.     //child process  
  3.     char * execve_str[] = {"env",NULL};  
  4.     char * env[] = {"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};  
  5.     if (execve("/usr/bin/env",execve_str,env) <0 ){  
  6.         perror("error on exec");  
  7.         exit(0);  
  8.     }  
  9. }else{  
  10.     //parent process  
  11.     wait(&childpid);  
  12.     printf("execve done\n\n");  
  13. }  

 

 

 

2.4 execl 函數

 

[cpp]  view plain  copy 
 
 在CODE上查看代碼片派生到我的代碼片
  1. if (fork() == 0){  
  2.     //child process  
  3.     if (execl("/usr/bin/echo","echo","executed by execl" ,NULL) <0 ){  
  4.         perror("error on exec");  
  5.         exit(0);  
  6.     }  
  7. }else{  
  8.     //parent process  
  9.     wait(&childpid);  
  10.     printf("execv done\n\n");  
  11. }  

 

 

2.5 execlp 函數

 

[cpp]  view plain  copy 
 
 在CODE上查看代碼片派生到我的代碼片
  1. if (fork() == 0){  
  2.     //child process  
  3.     if (execlp("echo","echo","executed by execlp" ,NULL) <0 ){  
  4.         perror("error on exec");  
  5.         exit(0);  
  6.     }  
  7. }else{  
  8.     //parent process  
  9.     wait(&childpid);  
  10.     printf("execlp done\n\n");  
  11. }  



 

2.6 execle 函數

 

[cpp]  view plain  copy 
 
 在CODE上查看代碼片派生到我的代碼片
    1. if (fork() == 0){  
    2.     //child process  
    3.     char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};  
    4.     if (execle("/usr/bin/env","env",NULL,env) <0){  
    5.         perror("error on exec");  
    6.         exit(0);  
    7.     }  
    8. }else{  
    9.     //parent process  
    10.     wait(&childpid);  
    11.     printf("execle done\n\n");  
    12. }  


免責聲明!

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



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