2012-02-23 168 views
8

在Linux 3.0/C++:的Linux 3.0:與管道標準輸入執行子進程/標準輸出

我想,做了以下的功能:

string f(string s) 
{ 
    string r = system("foo < s"); 
    return r; 
} 

顯然,上述方法無效,但你明白了。我有一個字符串s,我想作爲應用程序「foo」的子進程執行的標準輸入來傳遞,然後我想將其標準輸出記錄到字符串r,然後返回它。

我應該使用什麼linux系統調用或posix函數的組合?

+0

可能重複http://stackoverflow.com/questions/43116/how-can:也許孩子的DUP可以替換-i-run-an-external-program-from -c-and-parse-its-output) – 2012-02-23 01:54:46

+1

什麼是「Linux 3.0」? – Joe 2012-02-23 02:06:33

+0

@Joachim Pileborg:不,它不是重複的,我需要stdin和stdout。 popen是單向的(stdin或stdout,不是兩者)。 – 2012-02-23 02:10:18

回答

32

由eerpini提供的代碼不能按照書面形式工作。請注意,例如,之後使用在父項中關閉的管道末端。看看

close(wpipefd[1]); 

和隨後寫入該封閉描述符。這只是換位,但它顯示此代碼從未被使用過。以下是我測試過的一個版本。不幸的是,我改變了代碼風格,所以這不被接受爲eerpini代碼的編輯。

唯一的結構性變化是我只重定向子項中的I/O(注意dup2調用只在子路徑中)。這非常重要,否則父項I/O會混亂。感謝eerpini提供的初始答案,我在開發這個答案時使用了這個答案。

#define PIPE_READ 0 
#define PIPE_WRITE 1 

int createChild(const char* szCommand, char* const aArguments[], char* const aEnvironment[], const char* szMessage) { 
    int aStdinPipe[2]; 
    int aStdoutPipe[2]; 
    int nChild; 
    char nChar; 
    int nResult; 

    if (pipe(aStdinPipe) < 0) { 
    perror("allocating pipe for child input redirect"); 
    return -1; 
    } 
    if (pipe(aStdoutPipe) < 0) { 
    close(aStdinPipe[PIPE_READ]); 
    close(aStdinPipe[PIPE_WRITE]); 
    perror("allocating pipe for child output redirect"); 
    return -1; 
    } 

    nChild = fork(); 
    if (0 == nChild) { 
    // child continues here 

    // redirect stdin 
    if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1) { 
     exit(errno); 
    } 

    // redirect stdout 
    if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) { 
     exit(errno); 
    } 

    // redirect stderr 
    if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) { 
     exit(errno); 
    } 

    // all these are for use by parent only 
    close(aStdinPipe[PIPE_READ]); 
    close(aStdinPipe[PIPE_WRITE]); 
    close(aStdoutPipe[PIPE_READ]); 
    close(aStdoutPipe[PIPE_WRITE]); 

    // run child process image 
    // replace this with any exec* function find easier to use ("man exec") 
    nResult = execve(szCommand, aArguments, aEnvironment); 

    // if we get here at all, an error occurred, but we are in the child 
    // process, so just exit 
    exit(nResult); 
    } else if (nChild > 0) { 
    // parent continues here 

    // close unused file descriptors, these are for child only 
    close(aStdinPipe[PIPE_READ]); 
    close(aStdoutPipe[PIPE_WRITE]); 

    // Include error check here 
    if (NULL != szMessage) { 
     write(aStdinPipe[PIPE_WRITE], szMessage, strlen(szMessage)); 
    } 

    // Just a char by char read here, you can change it accordingly 
    while (read(aStdoutPipe[PIPE_READ], &nChar, 1) == 1) { 
     write(STDOUT_FILENO, &nChar, 1); 
    } 

    // done with these in this example program, you would normally keep these 
    // open of course as long as you want to talk to the child 
    close(aStdinPipe[PIPE_WRITE]); 
    close(aStdoutPipe[PIPE_READ]); 
    } else { 
    // failed to create child 
    close(aStdinPipe[PIPE_READ]); 
    close(aStdinPipe[PIPE_WRITE]); 
    close(aStdoutPipe[PIPE_READ]); 
    close(aStdoutPipe[PIPE_WRITE]); 
    } 
    return nChild; 
} 
+0

謝謝,最後是一段完整的工作代碼。 – minastaros 2017-05-12 12:53:19

2

由於您希望雙向訪問進程,因此您必須使用管道明確執行popen在後臺執行的操作。我不知道是否有任何這將在C更改++,但這裏是一個純C例如:

void piped(char *str){ 
    int wpipefd[2]; 
    int rpipefd[2]; 
    int defout, defin; 
    defout = dup(stdout); 
    defin = dup (stdin); 
    if(pipe(wpipefd) < 0){ 
      perror("Pipe"); 
      exit(EXIT_FAILURE); 
    } 
    if(pipe(rpipefd) < 0){ 
      perror("Pipe"); 
      exit(EXIT_FAILURE); 
    } 
    if(dup2(wpipefd[0], 0) == -1){ 
      perror("dup2"); 
      exit(EXIT_FAILURE); 
    } 
    if(dup2(rpipefd[1], 1) == -1){ 
      perror("dup2"); 
      exit(EXIT_FAILURE); 
    } 
    if(fork() == 0){ 
      close(defout); 
      close(defin); 
      close(wpipefd[0]); 
      close(wpipefd[1]); 
      close(rpipefd[0]); 
      close(rpipefd[1]); 
      //Call exec here. Use the exec* family of functions according to your need 
    } 
    else{ 
      if(dup2(defin, 0) == -1){ 
        perror("dup2"); 
        exit(EXIT_FAILURE); 
      } 
      if(dup2(defout, 1) == -1){ 
        perror("dup2"); 
        exit(EXIT_FAILURE); 
      } 
      close(defout); 
      close(defin); 
      close(wpipefd[1]); 
      close(rpipefd[0]); 
      //Include error check here 
      write(wpipefd[1], str, strlen(str)); 
      //Just a char by char read here, you can change it accordingly 
      while(read(rpipefd[0], &ch, 1) != -1){ 
        write(stdout, &ch, 1); 
      } 
    } 

} 

有效地這樣做:

  1. 創建管道和重定向標準輸出和標準輸入到端(注意在linux中,pipe()創建單向管道,所以你需要使用兩個管道來達到你的目的)。
  2. Exec現在將啓動一個新的進程,其中包含stdin和stdout的管道末端。
  3. 關閉未使用的描述符,將該字符串寫入管道,然後開始讀取任何進程可能轉儲到另一個管道的內容。

DUP()用於創建在文件描述符表中的重複條目。而dup2()更改描述符指向的內容。

注意:正如Ammo @在他的解決方案中所提到的,我上面提供的或多或少是一個模板,如果您只是試圖執行代碼,它就不會運行,因爲明顯存在exec *(函數族)丟失了,所以孩子將在fork()後立即終止。

+0

爲什麼在用dup2重定向時保留對dup的標準輸入/標準輸出的引用? dup2隱式關閉你的第二個參數。 – 2015-09-25 21:22:15

+1

你是對的,那是不必要的。上面的答案是正確的做法,請參閱。 – eerpini 2015-10-03 00:40:44

1

Ammo的代碼有一些錯誤處理錯誤。子進程在dup失敗後返回,而不是退出。

if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1 || 
     dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1 || 
     dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1 
     ) 
    { 
     exit(errno); 
    } 

    // all these are for use by parent only 
    close(aStdinPipe[PIPE_READ]); 
    close(aStdinPipe[PIPE_WRITE]); 
    close(aStdoutPipe[PIPE_READ]); 
    close(aStdoutPipe[PIPE_WRITE]); 
的[?我怎麼能運行在C外部程序,並解析其輸出(
+0

你是對的。子進程不應該從堆棧框架返回,也不應該嘗試打印錯誤。可能沒有人注意到,因爲這從來沒有發生過。 – 2017-08-30 23:08:59

+0

更新了上面的代碼 – 2017-08-30 23:47:21

相關問題