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 |
*/ |
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的使用:
假如我們想取得當前目錄下的文件個數,在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.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 來判斷後才使用此宏。