linux下代替system的基于管道的popen和pclose函數
linux下使用system需要謹慎,那么代替它的方法是什么呢?
標準I/O函數庫提供了popen函數,它啟動另外一個進程去執行一個shell命令行。
這里我們稱調用popen的進程為父進程,由popen啟動的進程稱為子進程。
popen函數還創建一個管道用于父子進程間通信。父進程要么從管道讀信息,要么向管道寫信息,至于是讀還是寫取決于父進程調用popen時傳遞的參數。下在給出popen、pclose的定義:
01#include <stdio.h>02/*03函數功能:popen()會調用fork()產生子進程,然后從子進程中調用/bin/sh -c來執行參數command的指令。04 參數type可使用“r”代表讀取,“w”代表寫入。05 依照此type值,popen()會建立管道連到子進程的標準輸出設備或標準輸入設備,然后返回一個文件指針。06 隨后進程便可利用此文件指針來讀取子進程的輸出設備或是寫入到子進程的標準輸入設備中07返回值:若成功則返回文件指針,否則返回NULL,錯誤原因存于errno中08*/09FILE * popen( const char * command,const char * type);1011/*12函數功能:pclose()用來關閉由popen所建立的管道及文件指針。參數stream為先前由popen()所返回的文件指針13返回值:若成功返回shell的終止狀態(也即子進程的終止狀態),若出錯返回-1,錯誤原因存于errno中14*/15int pclose(FILE * stream);
下面通過例子看下popen的使用:
假如我們想取得當前目錄下的文件個數,在shell下我們可以使用:
1ls | wc -l我們可以在程序中這樣寫:
01/*取得當前目錄下的文件個數*/02#include <stdio.h>03#include <stdlib.h>04#include <errno.h>05#include <sys/wait.h>0607#define MAXLINE 10240809int main()10{11 char result_buf[MAXLINE], command[MAXLINE];12 int rc = 0; // 用于接收命令返回值13 FILE *fp;1415 /*將要執行的命令寫入buf*/16 snprintf(command, sizeof(command), "ls ./ | wc -l");1718 /*執行預先設定的命令,并讀出該命令的標準輸出*/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 }3435 /*等待命令執行完畢并關閉管道及文件指針*/36 rc = pclose(fp);37 if(-1 == rc)38 {39 perror("關閉文件指針失敗");40 exit(1);41 }42 else43 {44 printf("命令【%s】子進程結束狀態【%d】命令返回值【%d】\r\n", command, rc, WEXITSTATUS(rc));45 }4647 return 0;48}$ 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 來判斷后才使用此宏。
更多內容請參考:shanzhizi專欄。
RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成