开发者

Supporting Piping (A Useful Hello World)

I am trying to write a collection of simple C++ programs that follow the basic Unix philosophy by:

  1. Make each program do one thing well.
  2. Expect the output of every program to become the input to another, as yet unknown, program.

I'm having an issue trying to get the output of one to be the input of the other, and getting the output of one be the input of a separate instance of itself. Very briefly, I have a program add which takes arguments and spits out the summation. I want to be able to pipe the output to another add instance.

./add 1 2 | ./add 3 4

That should yield 10 but currently yields 7.

I've encountered two problems:

  1. The cin waits for user input from the console. I don't want this, and haven't been able to find a simple example showing a the use of standard input stream without querying the user in the console. If someone knows of an example please let me know.
  2. I can't figure out how to use standard input while supporting piping. Currently, it appears it does not work. If I issue the command ./add 1 2 | ./add 3 4 it results in 7.

The relevant code is below:

add.cpp snippet

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    if (numbers.size() > 0)
    {
    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    std::cout << sum << std::endl;
  }
  else
  {
      double input;
      // right now this is test code while I try and get standard input streaming working as expected
      while (std::cin)
      {
            std::cin >> input;

            std::cout << input << std::endl;
      }

    }
// ... MORE IRRELEVANT CODE ...

So, I guess my question(s) is does anyone see what is incorrect with this code in order to support piping standard input? Are there some well known (or hidden) resources that explain clearly how to implement an example application supporting the basic Unix philosophy?

@Chris Lutz I've changed the code to what's below. The problem where cin still waits for user input on the console, and doesn't just take from the standard input passed from the pipe. Am I missing something trivial for handling this? I haven't tried Greg Hewgill's answer yet, but don't see how that would help since the issue is still with cin.

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    // right now this is test code while I try and get standard input streaming working as expected
    while (std::cin)
    {
          std::cin >> arg;

          std::cout << arg << std::endl;
    }

 开发者_如何学JAVA   std::cout << sum << std::endl;
// ... MORE IRRELEVANT CODE ...

EDIT: Almost there, this allows the output to be piped to the input, however it appears it's piping a duplicate. I'm issuing ./add 1 2 | ./add 3 4 and getting 13. With some extra cout statements in the while loop, I can see two values of 3 coming from cin when I only have one cout statement. Why am I getting a duplicate?

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    if (!isatty(fileno(stdin)))
    {
      while (std::cin)
      {
          std::cin >> arg; // this may require the use of std::strtod(), but to simplify the example I'm keeping it as this.

          sum += arg;
      }
    }

    std::cout << sum << std::endl;
// ... MORE IRRELEVANT CODE ...


Perhaps the problem is here:

if (numbers.size() > 0)

If you have any arguments, it adds them and ignores all piped data. So of course ./add 3 returns 3 - it has an argument, so it ignores the piped data.

You should fix your code to add both input (if input is given) and the arguments, not either-or. Remember: Command line arguments doesn't preclude piped input.


One function you might find useful is isatty(), which tells you whether a file descriptor is connected to an interactive session or not. You might use it like this:

if (!isatty(fileno(stdin))) {
    while (std::cin) {
        // ...
    }
}

This will only try to read input from the terminal if it's not interactive (meaning stdin is redirected from a file or pipe).


I would say the easiest is to ignore reading from stdin in your program. Instead, only let the program read from arguments, and call it like this: ./add 1 2 | xargs ./add 3 4

xargs will make the output from the first add an argument to the second add.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜