0

Does anyone know of a way to read in terminal output from bash commands into c++? For example, entering "ls" into the terminal returns the file names in the current directory, is there any way to perhaps import these names into a string or char array or something? I have been looking at fork(), exec(), and pipe(). I was attempting to open a pipe communication between child (system commands entered here) and parent (output read in here) but have been thoroughly unsuccessful. Ideas?

4
  • Thanks Kerrek SB, I had seen people using popen() in various ways but wasn't sure if that was the route to pursue or not. With the help of the documentation and putting together parts of how other people were using it, I came up with this. This does what the question I wrote asked about specifically, importing the terminal response from the command "ls" into a vector of file names and then I printed them to the screen. Each file name contains a newline character that would probably want to be removed. Commented Oct 13, 2011 at 12:08
  • FILE * pipe = popen( "ls", "r" );char buffer[256]; char command; vector<string> files; while ( fgets( buffer, sizeof buffer, pipe ) != NULL ) files.push_back( buffer ); pclose( pipe ); for( int fileNum = 0; fileNum < files.size(); fileNum++ ) cout << files[fileNum];` Commented Oct 13, 2011 at 12:09
  • Sorry!! I'm not even going to try again. I'll have to wait until I can answer my own question. Commented Oct 13, 2011 at 12:15
  • I haven't actually used popen() myself, and I'd have to look it up before I can say anything useful; I just thought it would be the right tool for the job. Do edit your question if you have a specific problem with it, though, and we can take a look! Commented Oct 13, 2011 at 13:32

2 Answers 2

2

Thanks Kerrek SB, I had seen people using popen() in various ways but wasn't sure if that was the route to pursue or not. With the help of the documentation and putting together parts of how other people were using it, I came up with this. This does what the question I wrote asked about specifically: importing the terminal response from the command "ls" into a vector of file names. However, each file name contains a newline character that I implicitly remove pushing back all but the last element of the string file into the vector.

void currentDirFiles( vector<string> & filesAddress )
{ 
    FILE * pipe = popen( "ls", "r" );
    char buffer[1000];
    string file;
    vector<string> files;
    while ( fgets( buffer, sizeof( buffer ), pipe ) != NULL )
    {
        file = buffer;
        files.push_back( file.substr( 0, file.size() - 1 ) );
    }
    pclose( pipe );
    filesAddress.swap( files );
}

I am trying to adapt the wikipedia RAII example to fit my needs as per the recommendations of Mankarse, and this is what I have so far. Does it look like I am on the right track? Any obvious mistakes or misunderstandings I have on this?

 #include <cstdio>

// exceptions
class file_error { } ;
class open_error : public file_error { } ;
class close_error : public file_error { } ;
class read_error : public file_error { } ;

class TerminalPipe
{
   public:
       TerminalPipe():
           pipeHandle(std::popen("ls", "r"))
           {
               if( pipeHandle == NULL )
                   throw open_error() ;
           } 
    ~TerminalPipe()
    {
        std::pclose(pipeHandle) ;
    } 
    void currentDirFiles( vector<string> & filesAddress )
    {
       char buffer[1000];
       string file;
       vector<string> files;
       while ( fgets( buffer, sizeof( buffer ), pipeHandle ) != NULL )
       {
           if( std::ferror( pipeHandle ) ) 
               throw read_error();
           else
           {
               file = buffer;
               files.push_back( file.substr( 0, file.size() - 1 ) );
           }
       }
    }
    private:
        std::FILE* pipeHandle ;
        // copy and assignment not implemented; prevent their use by
        // declaring private.
        TerminalPipe( const file & ) ;
        TerminalPipe & operator=( const file & ) ;
};

Then call it with this:

vector<string> dirFiles;
TerminalPipe pipe;    
pipe.currentDirFiles( dirFiles );
Sign up to request clarification or add additional context in comments.

7 Comments

This will leak the pipe if anything between FILE * pipe = popen( "ls", "r" ); and pclose( pipe ); throws (and four lines could...). You should wrap the FILE* up in some sort of RAII class.
@Mankarse I am reading about RAII and feeling a bit overwhelmed. I am just beginning to reach outside the introductory C++ I learned in lower division CS and reading about RAII is introducing a lot of new terminology I haven't heard before. I will try to update this code when I can get a handle on what I actually do. Thanks for the input it is clearly extremely valid.
@Mankarse Which four lines are you referring to by the way?
I was referring to string file;, vector<string> files;, file = buffer;, and files.push_back( file.substr( 0, file.size() - 1 ) );.
@Mankarse OK so I am trying to adapt the Wikipedia RAII example to fit my needs and above is what I have so far. Does this look like I'm on the right track or are there some things I am missing or misunderstanding? Thanks!!
|
0

If one uses glibmm c++ bindings one can execute the following

#include <glibmm.h>
#include <iostream>

#include <unistd.h>

void finished_process(int pid, int ret, Glib::RefPtr<Glib::MainLoop>& loop)
{
    loop->quit();
    std::cout << "Process finished: " << pid << "|" << ret <<
    std::endl;
}

int main()
{
    int stdout_fd, stdin_fd, stderr_fd, pid;
    Glib::ustring line;
    std::vector<std::string> command;

    Glib::init();
    command.push_back("ls");
    command.push_back("-l");
    command.push_back("|");
    command.push_back("grep");
    command.push_back("-i");
    command.push_back("my_file");

    try{
    Glib::spawn_async_with_pipes(".", command,
        Glib::SPAWN_SEARCH_PATH | Glib::SPAWN_DO_NOT_REAP_CHILD, sigc::slot<void>(),
        &pid, &stdin_fd, &stdout_fd, &stderr_fd);
    } catch(Glib::SpawnError &e){
    std::cout << "Error: " << e.what() << std::endl;
    return 0;
    }

    Glib::RefPtr<Glib::MainLoop> loop(Glib::MainLoop::create());
    Glib::RefPtr<Glib::MainContext> context = loop->get_context();
    context->signal_child_watch().connect(sigc::bind(sigc::ptr_fun(finished_process), loop),
                    pid);

    Glib::RefPtr<Glib::IOChannel> io = Glib::IOChannel::create_from_fd(stdout_fd);
    loop->run();

    while (io->read_line(line) != Glib::IO_STATUS_EOF){
    std::cout << line;
    }
    io->close();
    close(stdout_fd);
    std::cout << "Exit" << std::endl;
}

To compile the program g++ -g -std=c++0x signal_child_watch_example.cc -o signal_child_watch_example `pkg-config --libs --cflags giomm-2.4`

On Ubuntu/Debian one can install glibmm libraries as follows sudo apt-get install libglibmm-2.4-dev

Note In future this 2.4 version number may change. Make changes to the above command accordingly.

source: http://gtk.10911.n7.nabble.com/Glib-MainContext-signal-child-watch-is-not-working-td43517.html

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.