linux下代替system的基於管道的popen和pclose函數

linux下使用system須要謹慎,那麼代替它的方法是什麼呢?linux

標準I/O函數庫提供了popen函數,它啓動另一個進程去執行一個shell命令行。shell

這裏咱們稱調用popen的進程爲父進程,由popen啓動的進程稱爲子進程。函數

popen函數還建立一個管道用於父子進程間通訊。父進程要麼從管道讀信息,要麼向管道寫信息,至因而讀仍是寫取決於父進程調用popen時傳遞的參數。下在給出popen、pclose的定義:spa

 

01 #include <stdio.h>
02 /*
03 函數功能:popen()會調用fork()產生子進程,而後從子進程中調用/bin/sh -c來執行參數command的指令。
04         參數type可以使用「r」表明讀取,「w」表明寫入。
05         依照此type值,popen()會創建管道連到子進程的標準輸出設備或標準輸入設備,而後返回一個文件指針。
06         隨後進程即可利用此文件指針來讀取子進程的輸出設備或是寫入到子進程的標準輸入設備中
07 返回值:若成功則返回文件指針,不然返回NULL,錯誤緣由存於errno中
08 */
09 FILE * popen( const char * command,const char * type);
10  
11 /*
12 函數功能:pclose()用來關閉由popen所創建的管道及文件指針。參數stream爲先前由popen()所返回的文件指針
13 返回值:若成功返回shell的終止狀態(也即子進程的終止狀態),若出錯返回-1,錯誤緣由存於errno中
14 */
15 int pclose(FILE * stream);

 

下面經過例子看下popen的使用:.net

假如咱們想取得當前目錄下的文件個數,在shell下咱們可使用: 命令行

1 ls wc -l

咱們能夠在程序中這樣寫:指針

01 /*取得當前目錄下的文件個數*/
02 #include <stdio.h>
03 #include <stdlib.h>
04 #include <errno.h>
05 #include <sys/wait.h>
06  
07 #define MAXLINE 1024
08  
09 int main()
10 {
11     char result_buf[MAXLINE], command[MAXLINE];
12     int rc = 0; // 用於接收命令返回值
13     FILE *fp;
14  
15     /*將要執行的命令寫入buf*/
16     snprintf(command, sizeof(command), "ls ./ | wc -l");
17  
18     /*執行預先設定的命令,並讀出該命令的標準輸出*/
19     fp = popen(command, "r");
20     if(NULL == fp)
21     {
22         perror("popen執行失敗!");
23         exit(1);
24     }
25     while(fgets(result_buf, sizeof(result_buf), fp) != NULL)
26     {
27         /*爲了下面輸出好看些,把命令返回的換行符去掉*/
28         if('\n' == result_buf[strlen(result_buf)-1])
29         {
30             result_buf[strlen(result_buf)-1] = '\0';
31         }
32         printf("命令【%s】 輸出【%s】\r\n", command, result_buf);
33     }
34  
35     /*等待命令執行完畢並關閉管道及文件指針*/
36     rc = pclose(fp);
37     if(-1 == rc)
38     {
39         perror("關閉文件指針失敗");
40         exit(1);
41     }
42     else
43     {
44         printf("命令【%s】子進程結束狀態【%d】命令返回值【%d】\r\n", command, rc, WEXITSTATUS(rc));
45     }
46  
47     return 0;
48 }
編譯並執行:

$ gcc popen.ccode

$ ./a.outblog

命令【ls ./ | wc -l】 輸出【2】進程

命令【ls ./ | wc -l】子進程結束狀態【0】命令返回值【0】

上面popen只捕獲了command的標準輸出,若是command執行失敗,子進程會把錯誤信息打印到標準錯誤輸出,父進程就沒法獲取。好比,command命令爲「ls nofile.txt」 ,事實上咱們根本沒有nofile.txt這個文件,這時shell會輸出「ls: nofile.txt: No such file or directory」。這個輸出是在標準錯誤輸出上的。經過上面的程序並沒有法獲取。

注:若是你把上面程序中的command設成「ls nofile.txt」,編譯執行程序你會看到以下結果:

$ gcc popen.c 

$ ./a.out

ls: nofile.txt: No such file or directory

命令【ls nofile.txt】子進程結束狀態【256】命令返回值【1】

 須要注意的是第一行輸出並非父進程的輸出,而是子進程的標準錯誤輸出。

有時子進程的錯誤信息是頗有用的,那麼父進程怎麼才能獲取子進程的錯誤信息呢?

這裏咱們能夠重定向子進程的錯誤輸出,讓錯誤輸出重定向到標準輸出(2>&1),這樣父進程就能夠捕獲子進程的錯誤信息了。例如command爲「ls nofile.txt 2>&1」,輸出以下:

命令【ls nofile.txt 2>&1】 輸出【ls: nofile.txt: No such file or directory】

命令【ls nofile.txt 2>&1】子進程結束狀態【256】命令返回值【1】

附:子進程的終止狀態判斷涉及到的宏,設進程終止狀態爲status.

WIFEXITED(status)若是子進程正常結束則爲非0值。

WEXITSTATUS(status)取得子進程exit()返回的結束代碼,通常會先用WIFEXITED 來判斷是否正常結束才能使用此宏。

WIFSIGNALED(status)若是子進程是由於信號而結束則此宏值爲真。

WTERMSIG(status)取得子進程因信號而停止的信號代碼,通常會先用WIFSIGNALED 來判斷後才使用此宏。

WIFSTOPPED(status)若是子進程處於暫停執行狀況則此宏值爲真。通常只有使用WUNTRACED 時纔會有此狀況。

WSTOPSIG(status)取得引起子進程暫停的信號代碼,通常會先用WIFSTOPPED 來判斷後才使用此宏。

更多內容請參考:shanzhizi專欄

相關文章
相關標籤/搜索