歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> linux下代替system的基於管道的popen和pclose函數

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

日期:2017/3/3 11:21:25   编辑:Linux技術
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 來判斷後才使用此宏。
更多內容請參考:shanzhizi專欄
Copyright © Linux教程網 All Rights Reserved