3

http://pstreams.sourceforge.net/ pstreams is an apparently very simple library, re-implementing popen() for C++.

The library is very simple to install, consisting of only one single header file. You can download the header file here and add it to your application:
http://pstreams.cvs.sourceforge.net/viewvc/pstreams/pstreams/pstream.h?view=log

I thought what I wanted was pretty straightforward: send a command to the system and get its output. The home page of pstreams (above) and the documentation offer the following example:

redi::ipstream in("ls ./*.h");
std::string str;
while (in >> str) {
    std::cout << str << std::endl;
}

It seems to work well, but the code actually strips all spaces and carriage return from the output. Try this:

redi::ipstream in("ls -l"); /* The command has a slightly more complicated output. */
std::string str;
while (in >> str) {
    std::cout << str
}
std::cout << std::endl;

and we get a looong concatenated string without any space.

I did my best to understand the documentation, and the closest I have come to a solution is this:

 redi::ipstream in("ls -l");
 std::string str;
 while (!in.rdbuf()->exited()) {
 in >> str;
 }
 std::cout << str << std::endl;

However, the output is completely truncated at the first space.

I am stymied.

Related questions:
popen equivalent in c++
How to execute a command and get output of command within C++ using POSIX?

Note to moderators: a pstreams tag is required for this question and the other two above.

Community
  • 1
  • 1
augustin
  • 14,373
  • 13
  • 66
  • 79

3 Answers3

7

The streaming operator >> is designed to read a word at a time to a std::string, while skipping surrounding whitespace, just as you do when reading a number.

If you want to read an entire line of text at a time, use getline instead.

std::getline(in, str);
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • +1, thanks. I'm now going back both to the pstreams and to the std documentation to figure out where this is documented, and why I should have known it ;) See the full code solution in my answer. Thanks again. – augustin Mar 15 '11 at 08:34
  • instead of getting the whole stream content one line at a time (like in the code in my answer) is there a way to get everything at once? If not, why not? – augustin Mar 15 '11 at 08:46
  • 2
    It depends a bit on what the data looks like. By default, getline reads until finding an end-of-line '\n'. If you want it to stop somewhere else, you can add an extra parameter, like `getline(in, str, '\0');`, and it will read until it finds a '\0' or until the stream reaches its end-of-file state. – Bo Persson Mar 15 '11 at 09:34
3

The question is nothing to do with my pstreams library really, it's about istream and you'd get exactly the same behaviour reading from an ifstream or other istream. But the easiest way to read all the child process' output is:

redi::ipstream in("ls -l");
std::stringstream ss;
ss << in.rdbuf();
std::string s = ss.str();
  • +1, thanks. Again, I am a newbie. I did my homework and searched for a couple of days before asking, but obviously, I searched in the wrong places. It's really simple once you know how ;) Welcome at Stackoverflow. – augustin Mar 15 '11 at 11:00
0

Solution, based on Bo Persson's hint:

#include <pstream.h>
#include <iostream>
#include <stdio.h>

int main() {
        redi::ipstream in("ls -l");
        std::string str;
        std::string s;
        while (std::getline(in, s)) {
                str += s + '\n';
        }
        std::cout << str;
        return 0;
}

[Edit:] And an even simpler solution, again based on Bo's comments:

include <pstream.h>
#include <iostream>
#include <stdio.h>

int main() {
        redi::ipstream in("ls -l");
        std::string str;
        std::getline(in, str, '\0');
        std::cout << str;
        return 0;
}
augustin
  • 14,373
  • 13
  • 66
  • 79