Forking And Piping C++ Strange Ouput

2019-08-04 23:34发布

I am to write an assignment that takes 2 commands and their arguments (up to 5) and it will pipe the output of one to the other. It will then loop, again asking for two commands until quit is entered.

The problem I am having is that after entering values on the second loop, weird things happen, such as outputting "Enter Command 1" right after entering the second command (both appear on the same line). I also noticed that entering ls -l and then cat works for example, but entering ls -l then wc causes issues. Would someone mind taking a look and possibly helping me with it? I have been working at it all day and I have a little over an hour left to finish it.

Side Note: Yes, I realize the execute command setup is a bit stupid, but I am out of time and don't have time to fiddle with it. It works.

#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;
            }
    }   
}
}

Example Output:

nick@nick-VirtualBox ~/Documents/Assign10 $ ./z1615629
Command One (or quit): ls -l
Command Two: wc
Command One (or quit): Command One (or quit): quit

Example Output:

nick@nick-VirtualBox ~/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

2条回答
一纸荒年 Trace。
2楼-- · 2019-08-04 23:58

I know it doesn't address the question, but here's the original code C++-ified.

I've taken care to remain C++03 compatible. I only assume TR1 for the bind (allthough the incantation with mem_fun_ref should be pretty easy to write).

Note the reduction in size: removes 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;
                }
        }
    }
}

Disclaimer It compiles, I haven't run it (I wouldn't know what input to feed it, and how, anyway.)

查看更多
乱世女痞
3楼-- · 2019-08-05 00:11

Because you don't use functions, you have asymmetric behaviour in your command prompting and reading loops:

//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;
}

The second command only ever contains a single word. The newline is left behind, giving you the double prompt on the next cycle. Use getline(cin, input2) in the second prompt loop. If you use functions, you get consistency — functions make life easier, not harder.

You need to close the pipes in the parent process. You also need to wait() at:

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

using:

          default: // Parent process
               close(pipefd[0]);
               close(pipefd[1]);
               while (wait(0) != -1)
                   ;
               break;
查看更多
登录 后发表回答