c語言popen介紹


1、函數定義

#include <stdio.h>
FILE * popen(const char *command , const char *type ); int pclose(FILE *stream);

2、函數說明

  popen()函數通過創建一個管道,調用fork()產生一個子進程,執行一個shell以運行命令來開啟一個進程。這個管道必須由pclose()函數關閉,而不是fclose()函數。pclose()函數關閉標准I/O流,等待命令執行結束,然后返回shell的終止狀態。如果shell不能被執行,則pclose()返回的終止狀態與shell已執行exit一樣。

  type參數只能是讀或者寫中的一種,得到的返回值(標准I/O流)也具有和type相應的只讀或只寫類型。如果type是"r"則文件指針連接到command的標准輸出;如果type是"w"則文件指針連接到command的標准輸入。

  command參數是一個指向以NULL結束的shell命令字符串的指針。這行命令將被傳到bin/sh並使用-c標志,shell將執行這個命令。

  popen()的返回值是個標准I/O流,必須由pclose來終止。前面提到這個流是單向的(只能用於讀或寫)。向這個流寫內容相當於寫入該命令的標准輸入,命令的標准輸出和調用popen()的進程相同;與之相反的,從流中讀數據相當於讀取命令的標准輸出,命令的標准輸入和調用popen()的進程相同。

3、返回值

  如果調用fork()或pipe()失敗,或者不能分配內存將返回NULL,否則返回標准I/O流。popen()沒有為內存分配失敗設置errno值。如果調用fork()或pipe()時出現錯誤,errno被設為相應的錯誤類型。如果type參數不合法,errno將返回EINVAL。

4、參數說明:

  command: 是一個指向以 NULL 結束的 shell 命令字符串的指針。這行命令將被傳到 bin/sh 並使用 -c 標志,shell 將執行這個命令。

  mode: 只能是讀或者寫中的一種,得到的返回值(標准 I/O 流)也具有和 type 相應的只讀或只寫類型。如果 type 是 “r” 則文件指針連接到 command 的標准輸出;如果 type 是 “w” 則文件指針連接到 command 的標准輸入。

5、作用:

    popen函數允許一個程序將另外一個程序作為新進程來啟動,並可以傳遞數據或者通過它接受數據。

    其內部實現為調用 fork 產生一個子進程,執行一個 shell, 以運行命令來開啟一個進程,這個進程必須由 pclose() 函數關閉。

6、缺點:

    使用popen的不好影響是,針對每個popen調用,不僅要啟動一個被請求的程序,還要啟動一個shell,即每個popen調用將多啟動兩個進程。

例子1:  
 #include<stdio.h> #include<unistd.h> #include<string.h> int main() { FILE *fp=NULL; FILE *fh=NULL; char buff[128]={0}; memset(buff,0,sizeof(buff)); fp=popen("ls -l","r");//將命令ls-l 同過管道讀到fp fh=fopen("shell.c","w+");// 創建一個可寫的文件 fread(buff,1,127,fp);//將fp的數據流讀到buff中 fwrite(buff,1,127,fh);//將buff的數據寫入fh指向的文件中 pclose(fp); fclose(fh); return 0; }
[lol@localhost practice]$ ls
popen popen.c shell.c
[lol@localhost practice]$ cat shell.c
total 12
-rwxrwxr-x. 1 lol lol 5478 May 24 15:39 popen
-rw-rw-r--. 1 lol lol 473 May 24 15:39 popen.c
-rw-rw-r--. 1 lol lol  [lol@localhost practice]$ vim popen.c
[lol@localhost practice]$ 
例子2:
//
execute shell command//執行一個shell命令,輸出結果逐行存儲在resvec中,並返回行數 int32_t myexec(const char *cmd, vector<string> &resvec) { resvec.clear(); FILE *pp = popen(cmd, "r"); //建立管道 if (!pp) { return -1; } char tmp[1024]; //設置一個合適的長度,以存儲每一行輸出 while (fgets(tmp, sizeof(tmp), pp) != NULL) { if (tmp[strlen(tmp) - 1] == '\n') { tmp[strlen(tmp) - 1] = '\0'; //去除換行符 } resvec.push_back(tmp); } pclose(pp); //關閉管道 return resvec.size(); }
例子3:
int main()
{
    FILE *fpr = NULL, *fpw = NULL;
    char buf[256];
    int ret;

    fpr = popen("cat /etc/group", "r");  //執行完這行代碼,標准輸出就裝滿,這里這個標准輸出標記為out1,管道指向out1,fpr指向管道的讀端
    fpw = popen("grep root", "w");       //執行這句代碼,會一直去讀取標准輸出,若標准輸出為空,則會阻塞,直到標准輸出不為空,執行命令后又
                                         //會去指讀取標准輸出繼續執行。這里這個標准輸入標記為in2。
                                         //管道指向int2,fpw指向管道的寫端

    while ((ret = fread(buf, 1, sizeof(buf), fpr)) > 0)  //從out1中讀取256個字節數據,存放在buf
    {
        fwrite(buf, 1, ret, fpw);                        //將buf的數據寫到int2(此時gerp命令一直在獲取int2,直到進行退出)
    }

    pclose(fpr);
    pclose(fpw);
}

 

6、文件描述符

當某個進程打開文件時,操作系統返回相應的文件描述符,進程為了處理該文件必須引用此描述符。

所謂的文件描述符是一個低級的正整數。最前面的三個文件描述符(0,1,2)分別與標准輸入(stdin),標准輸出(stdout)和標准錯誤(stderr)對應

因此,函數 scanf() 使用 stdin,而函數 printf() 使用 stdout。你可以用不同的文件描述符改寫默認的設置並重定向進程的 I/O 到不同的文件。

popen函數的理解 

(1) popen(comm, type)函數會創建一個管道,再fork一個子進程,在子進程中執行execX函數來執行comm命令(因為execX執行新程序后新程序的進程空間會覆蓋原進程的進程空間,所以開一個子進程來執行execX家族函數),然后想要返回stdout或者stdin的文件指針(取決於type); 

(2) 因為comm命令是通過子進程的執行的,那么stdin或者stdout文件指針也是子進程的進程片空間的,要將其返回給父進程,這就需要管道了; 

(3) stdin是供程序寫數據的,stdout是供程序讀數據的。這里設計的巧妙之處在於,管道的讀端跟stdout綁定,管道的寫端跟stdin綁定; 

(4) 讀寫管道操作的無非就是管道(文件)的fd(文件描述符),這里將fd封裝到文件流指針fp中; 

(5) popen返回的是stdout,那么type為”r”,表示創建一個管道且該管道文件的讀端賦給fpr; 

(6) popen返回的是stdin,那么type為”w”,表示創建一個管道且該管道文件的寫端賦給fpw; 

(7) 這樣子,讀fpr(管道的讀端)等於讀子進程的stdout,寫fpw(管道的寫端)等於寫子進程的stdin。

 

參考:

https://www.jb51.net/article/140783.htm

https://blog.csdn.net/lihfqq/article/details/84687669

https://blog.csdn.net/hhhlizhao/article/details/71552588

https://blog.csdn.net/yellowston/article/details/90727424

https://blog.csdn.net/qq_29344757/article/details/70925123

 


免責聲明!

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



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