2012-11-20 67 views
1

我要寫一個需要2個命令及其參數(最多5個)的任務,它會將一個輸出傳遞給另一個。然後它會循環,再次詢問兩個命令,直到輸入quit。分叉和管道C++奇怪輸出

我遇到的問題是,在第二個循環中輸入值後,發生奇怪的事情,例如在輸入第二個命令(都出現在同一行)後立即輸出「Enter Command 1」。我還注意到,輸入ls -l然後cat例如,但輸入ls -l然後wc會導致問題。有人會介意看看,並可能幫助我嗎?我一整天都在努力工作,我還有一個多小時就完成了。

方面注意:是的,我意識到執行命令設置有點愚蠢,但我沒時間了,沒有時間擺弄它。有用。

#include <iostream> 
#include <string> 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <sstream> 
using namespace std; 

int main(){ 
//Our imput strings that the user enters. 
string input1; 
string input2; 

//Temporary string. 
string s; 

//Array to hold the items passed in. 
string arg1[6]; 
string arg2[6]; 

//A count of how many items they passed in. 
int carg1; 
int carg2; 

//Loop until quit. 
while(true){ 
    //Set all our values to empty/zero 
    carg1 = 0; 
    carg2 = 0; 
    input1.clear(); 
    input2.clear(); 

    //Prompt for first command. 
    while(input1.empty()){ 
     cout << "Command One (or quit): "; 
     getline(cin, input1); 
    } 

    //Split the string by the space to get the pieces of the command. 
    istringstream iss1(input1); 
    while (getline(iss1, s, ' ')) { 
     arg1[carg1] = s; 
     carg1++; 
    } 

    //Check if command is quit and exit if true. 
    if(arg1[0].compare("quit") == 0){ 
     return 0; 
    } 

    //Prompt for command 2. 
    while(input2.empty()){ 
     cout << "Command Two: "; 
     cin >> input2; 
    } 

    //Once again, split based on spaces. 
    istringstream iss2(input2); 
    while (getline(iss2, s, ' ')) { 
     //arg2.push_front(s); 
     arg2[carg2] = s; 
     carg2++; 
    } 

    //Initialize the pipe. 
    int pipefd[2]; 
    if(pipe(pipefd) == -1){ 
     perror("Pipe"); 
     exit(EXIT_FAILURE); 
    } 

    //Create the fork to two processes. 
    int pid = fork(); 

    //Switch to check for parent and child. 
    switch(pid){ 
     case 0: //Child process 
      //Close the read pipe and standard input. 
      close(pipefd[0]); 
      close(1); 

      //Copy data 
      dup(pipefd[1]); 

      //Close other end of the pipe 
      close(pipefd[1]); 

      //Execute the first command. Based on how many params, call different ones. 
      switch(carg1){ 
       case 1: 
        execlp(arg1[0].c_str(), arg1[0].c_str(), (char*)NULL); 
        break; 
       case 2: 
        execlp(arg1[0].c_str(), arg1[0].c_str(), arg1[1].c_str(), (char*)NULL); 
        break; 
       case 3: 
        execlp(arg1[0].c_str(), arg1[0].c_str(), arg1[1].c_str(), arg1[2].c_str(), (char*)NULL); 
        break; 
       case 4: 
        execlp(arg1[0].c_str(), arg1[0].c_str(), arg1[1].c_str(), arg1[2].c_str(), arg1[3].c_str(), (char*)NULL); 
        break; 
       case 5: 
        execlp(arg1[0].c_str(), arg1[0].c_str(), arg1[1].c_str(), arg1[2].c_str(), arg1[3].c_str(), arg1[4].c_str(), (char*)NULL); 
        break; 
       case 6: 
        execlp(arg1[0].c_str(), arg1[0].c_str(), arg1[1].c_str(), arg1[2].c_str(), arg1[3].c_str(), arg1[4].c_str(), arg1[5].c_str(), (char*)NULL); 
        break; 
       case 7: 
        execlp(arg1[0].c_str(), arg1[0].c_str(), arg1[1].c_str(), arg1[2].c_str(), arg1[3].c_str(), arg1[4].c_str(), arg1[5].c_str(), arg1[6].c_str(), (char*)NULL); 
        break; 
      } 
      return 0; 
     case -1: //Error 
      perror("fork"); 
      exit(EXIT_FAILURE); 
     default: //Parent Process 
      //Wait for initial command to execute. 
      wait(&pid); 

      //Fork into two processes. 
      int pid2 = fork(); 

      //Switch based on child and parent. 
      switch(pid2){ 
       case 0: //Child process 
        //Close write end of pipe and standard output. 
        close(pipefd[1]); 
        close(0); 

        //Duplicate to standard input 
        dup(pipefd[0]); 

        //Close read end. 
        close(pipefd[0]); 

        //Execute proper command based on params 
        switch(carg2){ 
         case 1: 
          execlp(arg2[0].c_str(), arg2[0].c_str(), (char*)NULL); 
          break; 
         case 2: 
          execlp(arg2[0].c_str(), arg2[0].c_str(), arg2[1].c_str(), (char*)NULL); 
          break; 
         case 3: 
          execlp(arg2[0].c_str(), arg2[0].c_str(), arg2[1].c_str(), arg2[2].c_str(), (char*)NULL); 
          break; 
         case 4: 
          execlp(arg2[0].c_str(), arg2[0].c_str(), arg2[1].c_str(), arg2[2].c_str(), arg2[3].c_str(), (char*)NULL); 
          break; 
         case 5: 
          execlp(arg2[0].c_str(), arg2[0].c_str(), arg2[1].c_str(), arg2[2].c_str(), arg2[3].c_str(), arg2[4].c_str(), (char*)NULL); 
          break; 
         case 6: 
          execlp(arg2[0].c_str(), arg2[0].c_str(), arg2[1].c_str(), arg2[2].c_str(), arg2[3].c_str(), arg2[4].c_str(), arg2[5].c_str(), (char*)NULL); 
          break; 
         case 7: 
          execlp(arg2[0].c_str(), arg2[0].c_str(), arg2[1].c_str(), arg2[2].c_str(), arg2[3].c_str(), arg2[4].c_str(), arg2[5].c_str(), arg2[6].c_str(), (char*)NULL); 
          break; 
        } 
        return 0; 
       case -1: //Error 
        perror("fork"); 
        exit(EXIT_FAILURE); 
       default: //Parent Process 
        //wait(&pid2); 
        break; 
      } 
    } 
} 
} 

示例輸出

[email protected] ~/Documents/Assign10 $ ./z1615629 
Command One (or quit): ls -l 
Command Two: wc 
Command One (or quit): Command One (or quit): quit 

示例輸出

[email protected] ~/Documents/Assign10 $ ./z1615629 
Command One (or quit): ls -l 
Command Two: cat 
Command One (or quit): Command One (or quit): 
total 32 
-rwxr-xr-x 1 nick nick 13358 Nov 20 15:46 z1615629 
-rw-r--r-- 1 nick nick 4544 Nov 20 15:46 z1615629.cxx 
-rw-r--r-- 1 nick nick 8104 Nov 20 15:46 z1615629.o 
+0

請將問題解壓縮爲一個自包含的工作示例,以便我們可以看到展示的問題。這裏消化太多了。 – 2012-11-20 21:56:39

+0

使用'execvp()'而不是'execlp()'用switch和'execlp()'來刪除大部分無意義的重複的胡扯。而且,爲了大聲哭泣,不要多次寫這種代碼;使用一個函數來避免重複你自己。 –

+0

我明白,通常我會寫一個函數,但我非常忙,我從來沒有處理過管道。有時候,如果你非常忙於截止日期,你不會認真思考代碼。另外,我很少用C++編寫。無論如何,會嘗試。 – user1840230

回答

1

因爲你不使用的功能,你必須在你的命令不對稱行爲提示和閱讀循環:

//Prompt for first command. 
while (input1.empty()){ 
    cout << "Command One (or quit): "; 
    getline(cin, input1); 
} 

... 

//Prompt for command 2. 
while (input2.empty()){ 
    cout << "Command Two: "; 
    cin >> input2; 
} 

第二個命令只包含一個單詞。換行符被留下,​​在下一個循環中提供雙重提示。在第二個提示循環中使用getline(cin, input2)。如果你使用功能,你會得到一致性 - 功能使生活更輕鬆,而不是更難。

您需要關閉父進程中的管道。您還需要在wait()

  default: //Parent Process 
       //wait(&pid2); 
       break; 

使用:

  default: // Parent process 
       close(pipefd[0]); 
       close(pipefd[1]); 
       while (wait(0) != -1) 
        ; 
       break; 
+1

我正在將您的答案作爲正確的答案,儘管問題的一部分也是從閱讀行中的最後一個字符開始在文本輸入後自動觸發我的「請輸入cmd 1」消息。儘管如此,非常感謝您的幫助,我能夠按時完成任務,儘管幾乎沒有。將來,我一定會從一開始就爲它做功能。 – user1840230

+0

@ user1840230:我希望你告訴你的教授關於StackOverflow :) – 2012-11-21 13:08:40

1

我知道這並沒有解決這個問題,但這裏的原代碼C++ - 指明分數。

我已經注意保持C++ 03兼容。我只假設TR1爲綁定(all_memory_fun_ref的咒語應該很容易寫)。

注意尺寸的減小:刪除88 LOC(> 50%)

#include <iostream> 
#include <string> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <sstream> 
#include <vector> 
#include <algorithm> 
#include <iterator> 
#include <tr1/functional> 
using namespace std; 

void execute(std::vector<string> const& arg1) { 
    vector<char const*> argv(arg1.size()); 
    transform(arg1.begin(), arg1.end(), argv.begin(), tr1::bind(&std::string::c_str, tr1::placeholders::_1)); 
    argv.push_back(NULL); 
    execvp(argv[0], (char* const*) &argv[0]); // TODO FIXME error handling? 
    exit(EXIT_FAILURE); 
} 

std::vector<string> getinput(std::string prompt) { 
    vector<string> result; 
    string input; 
    //Prompt for first command. 
    while(input.empty()) { 
     cout << prompt; 
     getline(cin, input); 
    } 
    //Split the string by the space to get the pieces of the command. 
    { 
     istringstream iss(input); 
     copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(result)); 
    } 
    return result; 
} 

int main() { 
    //Loop until quit. 
    while(true) { 
     const std::vector<string> cmd1 = getinput("Command One (or quit): "); 
     if(cmd1.empty() || cmd1[0] == "quit") { 
      return 0; 
     } 
     const std::vector<string> cmd2 = getinput("Command Two: "); 

     //Initialize the pipe. 
     int pipefd[2]; 
     if(pipe(pipefd) == -1) { 
      perror("Pipe"); 
      exit(EXIT_FAILURE); 
     } 
     int pid = fork();    //Create the fork to two processes. 
     switch(pid) {     // Switch to check for parent and child. 
      case 0:      // Child process 
       close(pipefd[0]);  // Close the read pipe and standard input. 
       close(1); 
       dup(pipefd[1]);   // Copy data 
       close(pipefd[1]);  // Close other end of the pipe 
       execute(cmd1); 
       return 0; 
      case -1:     // Error 
       perror("fork"); 
       exit(EXIT_FAILURE); 
      default:     // Parent Process 
       wait(&pid);    // Wait for initial command to execute. 
       int pid2 = fork();  // Fork into two processes. 
       switch(pid2) {   // Switch based on child and parent. 
       case 0:     // Child process 
        close(pipefd[1]); // Close write end of pipe and standard output. 
        close(0); 
        dup(pipefd[0]);  // Duplicate to standard input 
        close(pipefd[0]); // Close read end. 
        execute(cmd2);  // Execute proper command based on params 
        return 0; 
       case -1:    // Error 
        perror("fork"); 
        exit(EXIT_FAILURE); 
       default:    // Parent Process 
        //wait(&pid2); 
        break; 
       } 
     } 
    } 
} 

免責聲明它編譯,我還沒有運行它(我不知道什麼樣的輸入給它餵食,以及如何操作。)

+0

確保在'execvp()'之後有'exit(EXIT_FAILURE);'或者等價物。讓孩子'返回'(就像'execvp()'失敗那樣)將會是一個小小的災難,因爲你有兩個進程試圖提示並從標準輸入讀取,導致混亂和混亂。 –

+0

+1:我取得了與您相同的結果,但採用了C-in-C++風格。我比C++程序員更像C程序員。我小心翼翼地沒有顯示你的'execute()'的工作等價物 - 但我通過編寫代碼刪除了很多代碼。 –

+0

@JonathanLeffler哈。你也許是對的。然而,我所嘗試的只是OP代碼的1:1重構。我很高興我管理了'FIXME TODO'評論(現在,在你的煽動中,現在已經成功了)_) – sehe