Linux C/C++調用shell命令后獲取shell返回值
項目中C/C++調用shell命令后,某系處理返回值的過程是以“臨時文件”的方式進行;即shell命令執行后將返回值存放在臨時文件(如temp.txt),C/C++程序再訪問文件,獲取shell的返回值。最經典的就是調用WiFi(iwlist wlan0 scan )掃描指令查詢WiFi節點,然后解析獲取WiFi數量、名稱、信號強度、加密方式等信息。
通過“臨時文件”的方式交互數據,是比較簡單、易用和易理解的方式,在多進程間、多線程間也可以使用,但一般不會使用。共享“臨時文件”有個弊端就是效率上不比較低,創建文件、刪除文件然后是訪問,都是訪問存儲器(磁盤、flash),加上文件系統的一層封裝和存儲介質映射,訪問速度不如訪問內存快。
“臨時文件”的方式,個人覺得不是很好,通過該案例總結下C/C++調用shell命令知識。
1.C/C++調用shell命令方式
Linux 系統中使用 C/C++ 調用 shell 命令常用方式:
- system()函數
- popen()函數
- exec函數簇
system()函數最常用,簡單高效; popen() 執行 shell 命令的開銷比 system() 小;system()和popen()都封裝了進程創建、釋放,內部實質調用的是exec函數簇;exec需手動fork進程進,然后再調用exec函數簇個,過程比前兩者稍微復雜。
1.1 system()
C語言執行linux shell命令,對於沒有返回結果的,可直接使用system()函數,對於有返回結果的,可以用popen命令,對其封裝后,可以獲取相應的返回信息。
函數原型:
FILE * popen ( const char * command , const char * type );
int pclose ( FILE * stream );
command:要執行shell命令
type:創建的管道的讀寫類型("r" 或者 "w")
1.type為“r”時,管道連接到shell子進程的標准輸出,
2.type為“w”時,管道連接到shell子進程的標准輸入
“r”就能獲取shell命令的執行輸出結果了。返回值為FILE *文件指針,使用fread即可從文件流指針
中讀出輸出結果。
實例:
#include <stdio.h>
int main(void)
{
FILE *fp = NULL;
char buf[100]={0};
fp = popen("ps", "r");
if(fp) {
int ret = fread(buf,1,sizeof(buf)-1,fp);
if(ret > 0) {
printf("%s",buf);
}
pclose(fp);
printf("\n");
}
return 0;
}
1.popen()會調用fork()產生子進程,然后從子進程中調用/bin/sh -c來執行參數command的指令。
2.popen函數還創建一個管道用於父子進程間通信。子進程要么從管道讀信息,要么向管道寫信息,至於是讀
還是寫取決於父進程調用popen時傳遞的參數。
3.pclose()用來關閉由popen所建立的管道及文件指針
popen是不堵塞的,也就是說不會等待子進程的結束並殺死子進程,即不會管理進程。這樣就需要人為的去
殺死或忽略子進程等操作。還有就是popen會將執行的結果返回到buffer中。在執行期間調用進程會一直等
待shell命令執行完成。popen :沒有對信號做任何的處理。popen()函數中沒有屏蔽SIGINT、SIGQUIT的
原因是因為popen是”並行的”,不能影響其它”並行”進程。
system是堵塞的,完成后會自動對進程進行管理,無需再去對進程進行管理。另外,system不會返回執行的
結果,只是會返回執行是否成功。system:對SIGCHLD、SIGINT、SIGQUIT都做了處理,system()調用對
信號屏蔽的原因是因為system能夠及時的退出並且能夠正確的獲取子進程的退出狀態(成功回收子進程)。
主要區別:system函數調用shell命令,但是無法獲得運行的shell命令執行的輸出結果。而使用popen
能夠獲取到輸出結果。 popen后需要調用pclose防止子進程變成”僵屍”狀態。