2008-09-24 68 views
50

我試圖通過system()啓動外部應用程序 - 例如,system("ls")。我想捕獲它的輸出,所以我可以將它發送到另一個函數進行進一步處理。在C/C++中做這件事的最好方法是什麼?以最佳方式從系統()命令捕獲stdout

+0

你最好是什麼意思?從我的回答中,我會說最優的可能取決於每種情況。 fork/exec/dup2/STDOUT_FILENO方法可能適用於特殊情況。 – nephewtom 2014-10-03 10:43:47

回答

0

我不完全確定它可能在標準C中,因爲兩個不同的進程通常不共享內存空間。我能想到的最簡單的方法是讓第二個程序將其輸出重定向到一個文本文件(程序名> textfile.txt),然後讀取該文本文件以進行處理。但是,這可能不是最好的方法。

6

編輯:誤讀爲想將輸出傳遞給另一個程序,而不是另一個函數。 popen()幾乎肯定是你想要的。

系統爲您提供對shell的完全訪問權限。如果你想繼續使用它,你可以通過系統(「ls> tempfile.txt」)將其輸出重定向到一個臨時文件,但選擇一個安全的臨時文件是一件痛苦的事情。或者,你甚至可以通過另一個程序重定向它:system(「ls | otherprogram」);

有些人可能會推薦popen()命令。這是你想要的,如果你能處理輸出自己:

FILE *output = popen("ls", "r"); 

,這將給你一個文件指針,你可以用下面的命令對輸出它讀取。您可以使用pipe()調用創建一個與fork()組合的連接,以創建新的進程,dup2()更改它們的標準輸入和輸出,exec()運行新的程序,並在主程序中等待()以等待它們。這只是設置管道很像殼。有關詳細信息和示例,請參閱pipe()手冊頁。

1

在Windows中,不是使用system(),而是使用CreateProcess,將輸出重定向到管道並連接到管道。

我猜這也是可能的一些POSIX的方式?

40

從POPEN手冊:

#include <stdio.h> 

FILE *popen(const char *command, const char *type); 

int pclose(FILE *stream); 
+1

FILE * stream = popen(const char * command,const char * type); – Arpith 2013-08-26 12:25:24

+5

我想補充說這隻適用於Posix系統。在我的Windows 7版本中,沒有popen功能,但是有一個可用的_popen。 – user1505520 2014-07-30 20:25:39

30

嘗試POPEN()函數。它執行一個命令,如system(),但將輸出導向一個新文件。返回一個指向流的指針。

FILE *lsofFile_p = popen("lsof", "r"); 

    if (!lsofFile_p) 
    { 
    return -1; 
    } 

    char buffer[1024]; 
    char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p); 
    pclose(lsofFile_p); 
1

功能popen()pclose()可能是你在找什麼。

查看glibc manual的示例。

3

函數popen()等沒有重定向stderr等;我爲此寫了popen3()

1

其實,我只是檢查和:

  1. POPEN是有問題的,因爲該過程分叉。所以如果你需要等待shell命令執行,那麼你就有可能錯過它。就我而言,我的程序在管道開始工作之前就已經關閉了。

  2. 我結束了使用系統在linux上使用tar命令調用。來自系統的返回值是焦油的結果。

所以:如果需要返回值,則不是沒不僅沒有必要使用POPEN,它可能不會做你想做的。

+1

問題要求捕獲*輸出*,而不是*退出代碼*。所以我不認爲你提到的問題出現了。 – 2014-12-21 16:41:50

2

最有效的方法是直接用stdout文件描述符,繞過FILE流:

pid_t popen2(const char *command, int * infp, int * outfp) 
{ 
    int p_stdin[2], p_stdout[2]; 
    pid_t pid; 

    if (pipe(p_stdin) == -1) 
     return -1; 

    if (pipe(p_stdout) == -1) { 
     close(p_stdin[0]); 
     close(p_stdin[1]); 
     return -1; 
    } 

    pid = fork(); 

    if (pid < 0) { 
     close(p_stdin[0]); 
     close(p_stdin[1]); 
     close(p_stdout[0]); 
     close(p_stdout[1]); 
     return pid; 
    } else if (pid == 0) { 
     close(p_stdin[1]); 
     dup2(p_stdin[0], 0); 
     close(p_stdout[0]); 
     dup2(p_stdout[1], 1); 
     dup2(::open("/dev/null", O_WRONLY), 2); 

     /// Close all other descriptors for the safety sake. 
     for (int i = 3; i < 4096; ++i) { 
      ::close(i); 
     } 

     setsid(); 
     execl("/bin/sh", "sh", "-c", command, NULL); 
     _exit(1); 
    } 

    close(p_stdin[0]); 
    close(p_stdout[1]); 

    if (infp == NULL) { 
     close(p_stdin[1]); 
    } else { 
     *infp = p_stdin[1]; 
    } 

    if (outfp == NULL) { 
     close(p_stdout[0]); 
    } else { 
     *outfp = p_stdout[0]; 
    } 

    return pid; 
} 

孩子使用popen2()讀輸出是這樣的:

int child_stdout = -1; 
pid_t child_pid = popen2("ls", 0, &child_stdout); 

if (!child_pid) { 
    handle_error(); 
} 

char buff[128]; 
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff)); 

爲了寫和閱讀:

int child_stdin = -1; 
int child_stdout = -1; 
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout); 

if (!child_pid) { 
    handle_error(); 
} 

const char text = "1\n2\n123\n3"; 
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1); 

char buff[128]; 
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));