1. 程式人生 > >基於管道的popen和pclose函式

基於管道的popen和pclose函式

基於管道的popen和pclose函式

https://my.oschina.net/renhc/blog/35116

 

標準I/O函式庫提供了popen函式,它啟動另外一個程序去執行一個shell命令列。

這裡我們稱呼叫popen的程序為父程序,由popen啟動的程序稱為子程序。

popen函式還建立一個管道用於父子程序間通訊。父程序要麼從管道讀資訊,要麼向管道寫資訊,至於是讀還是寫取決於父程序呼叫popen時傳遞的引數。下在給出popen、pclose的定義:

 

#include <stdio.h>
/*
函式功能:popen()會呼叫fork()產生子程序,然後從子程序中呼叫/bin/sh -c來執行引數command的指令。
        引數type可使用“r”代表讀取,“w”代表寫入。
        依照此type值,popen()會建立管道連到子程序的標準輸出裝置或標準輸入裝置,然後返回一個檔案指標。
        隨後程序便可利用此檔案指標來讀取子程序的輸出裝置或是寫入到子程序的標準輸入裝置中
返回值:若成功則返回檔案指標,否則返回NULL,錯誤原因存於errno中
*/
FILE * popen( const char * command,const char * type);

/*
函式功能:pclose()用來關閉由popen所建立的管道及檔案指標。引數stream為先前由popen()所返回的檔案指標
返回值:若成功返回shell的終止狀態(也即子程序的終止狀態),若出錯返回-1,錯誤原因存於errno中
*/
int pclose(FILE * stream);

 

下面通過例子看下popen的使用:

假如我們想取得當前目錄下的檔案個數,在shell下我們可以使用: 

ls | wc -l

我們可以在程式中這樣寫:

/*取得當前目錄下的檔案個數*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>

#define MAXLINE 1024

int main()
{
	char result_buf[MAXLINE], command[MAXLINE];
	int rc = 0; // 用於接收命令返回值
	FILE *fp;

	/*將要執行的命令寫入buf*/
	snprintf(command, sizeof(command), "ls ./ | wc -l");

	/*執行預先設定的命令,並讀出該命令的標準輸出*/
	fp = popen(command, "r");
	if(NULL == fp)
	{
		perror("popen執行失敗!");
		exit(1);
	}
	while(fgets(result_buf, sizeof(result_buf), fp) != NULL)
	{
		/*為了下面輸出好看些,把命令返回的換行符去掉*/
		if('\n' == result_buf[strlen(result_buf)-1])
		{
			result_buf[strlen(result_buf)-1] = '\0';
		}
		printf("命令【%s】 輸出【%s】\r\n", command, result_buf);
	}

	/*等待命令執行完畢並關閉管道及檔案指標*/
	rc = pclose(fp);
	if(-1 == rc)
	{
		perror("關閉檔案指標失敗");
		exit(1);
	}
	else
	{
		printf("命令【%s】子程序結束狀態【%d】命令返回值【%d】\r\n", command, rc, WEXITSTATUS(rc));
	}

	return 0;
}

編譯並執行:

$ gcc popen.c

$ ./a.out

命令【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 來判斷後才使用此巨集。