Pipeline Support (a helpful welcome world)

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

  • Do each program well.
  • Expect the output of each program, which will be the entrance to another, yet unknown, program.

I had a problem with trying to get the output of one of them to input the other, and getting the result of one of them is the contribution of a separate instance. Very shortly, I have an add program that takes arguments and spits out the summation. I want to be able to connect the output to another instance to add .

./add 1 2 | ./add 3 4 

That should give 10 , but currently gives 7 .

I ran into two problems:

  • cin is waiting for user input from the console. I do not want this and could not find a simple example showing the use of a standard input stream without a user request in the console. If anyone knows an example, please let me know.
  • I cannot figure out how to use standard input with piping support. Currently, it does not seem to be working. If I issue the command ./add 1 2 | ./add 3 4 , this results in 7.

The corresponding code is given 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's wrong with this code to support standard piping input? Are there any well-known (or hidden) resources that clearly explain how to implement an example application that supports the basic Unix philosophy ?

@ Chris Lutz I changed the code to the one below. The problem is when cin is still waiting for user input to the console and does not just take from standard input transferred from the channel. Have I really missed something trivial for this? I have not tried to answer Greg Huglill yet, but I do not understand how this will help, since the problem is still related to 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; } std::cout << sum << std::endl; // ... MORE IRRELEVANT CODE ... 

EDIT:. Almost there, it allows output to be output, however it seems to duplicate the duplicate. I release ./add 1 2 | ./add 3 4 and get 13 . With some additional cout statements in the while loop, I see two 3 values ​​coming from cin when I have only 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 ... 
+4
source share
3 answers

Perhaps the problem is here:

 if (numbers.size() > 0) 

If you have any arguments, it adds them and ignores all data on the channels. Therefore, of course, ./add 3 returns 3 - it has an argument, so it ignores the data transmitted over the channels.

You must correct your code to add both input (if input is specified) and arguments, rather than either - or. Remember: command line arguments do not exclude the input channel.

+4
source

One useful function is isatty() , which indicates whether a file descriptor is connected to an interactive session or not. You can use it as follows:

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

This will only try to read input from the terminal if it is not interactive (this means that stdin is redirected from a file or channel).

+2
source

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

xargs will infer from the first add argument to the second add .

+1
source

Source: https://habr.com/ru/post/1333517/


All Articles