Search code examples
c++linuxexecforkpipe

Run process with string input and output


There are plenty of questions on here related to fork() and exec(). I have not found one that really makes the process of using them simple though, and making programmer's lives simple is the goal.

I need a C++, linux-friendly function that does the following:

string RunCommand(string command, string input){}

This function should be able to run a shell command, like grep, and "pipe" the content of input into it and read the ouptut and return it. So if I would do the following at the command line:

ps -elf | grep somequerytext

I would in code do:

string psOutput = RunCommand("ps -elf","");
string grepOutput = RunCommand("grep somequerytext", psOutput);

*edit: The question is what is the best implementation of the RunCommand function.

*edit: popen was considered as a solution for simplicity, but popen restricts you to piping data in or piping data out, but not both.


Solution

  • It appears that you need a function to:

    • Create two pipes and fork.
    • The child process then does:
      • Duplicate appropriate descriptors of the pipes so that one file descriptor is standard input and one standard output
      • Close the pipe descriptors
      • Split up the command string into arguments
      • Run the command with the arguments
    • The parent (main) process then does:
      • Close the appropriate pipe file descriptors
      • Writes the input string to the child and closes the pipe to the child's standard input
      • Reads the output string from the child
      • Closes the the pipe from the child's standard output
      • Waits for the child to die
    • When the child is dead, the main process can continue, returning the string that it read.

    The only potential problem with this outline is if the child writes output before it is finished reading its input, and it writes so much output that the pipe is full (they have a finite and usually quite small capacity). In that case, the processes will deadlock - the parent trying to write to the child, and the child trying to write to the parent, and both stuck waiting for the other to read some data. You can avoid that by having two threads in the parent, one processing the writing, the other processing the reading. Or you can use two child processes, one to run the command and one to write to the standard input, while the parent reads from the command's standard output into a string.

    One of the reasons there isn't a standard function to do this is precisely the difficulty of deciding what are the appropriate semantics.

    I've ignored error handling and signal handling issues; they add to the complexity of it all.