views:

409

answers:

8

Hi All,

I'm writing a program in C++ which do some special treatment for all the files in the current directory on Linux OS.

So i was thinking of using system calls such as system("ls") to get the list of all files.

but how to store it then inside my program ? ( how to redirect the output of ls to let's say a string that i declared in the program )

Thanks

+4  A: 

I suggest you don't call out to ls - do the job properly, using opendir/readdir/closedir to directly read the directory.

Code example, print directory entries:

// $ gcc *.c && ./a.out 
#include <stdlib.h> // NULL
#include <stdio.h>  
#include <dirent.h>

int main(int argc, char* argv[]) {
  const char* path = argc <= 1 ? "." : argv[1];

  DIR* d = opendir(path);
  if (d == NULL) return EXIT_FAILURE;

  for(struct dirent *de = NULL; (de = readdir(d)) != NULL; )
    printf("%s/%s\n", path, de->d_name);

  closedir(d);
  return 0;
}
Douglas Leeder
@J.F. Sebastian - Thanks.
Douglas Leeder
Be aware that you shouldn't use `readdir` in a multi-threaded program. Instead you should use `readdir_r`
Douglas Leeder
+5  A: 

I don't think you can use system to read the output.

Try using popen.

   #include <stdio.h>

   FILE *popen(const char *command, const char *type);

   int pclose(FILE *stream);

But, you probably don't want to do this for ls. Two reasons:

  • If you want to get a directory listing use the file system api directly (readdir), instead of using shell commands and parsing it.
  • Someone pointed me to a blog post recently explaining why parsing ls output is a bad idea.
Stephen
I think it would be a good addition to explain why that would not work for `ls`.
RaphaelSP
@RaphaelSP: I had seen another answer describing not to use ls, but wanted to answer the system vs popen question. Point taken though, so I've added a better justification.
Stephen
Wow, you've done it right :-). +2 if I could...
RaphaelSP
Hehe, thanks :)
Stephen
A: 

Are you on UNIX or Windows? On UNIX you would create a child process with the fork() call, and then create some pipes and assign the STDOUT of the child process to the STDIN of the parent process. On Windows there is bound to be something similar in the Win32 API.

Dr. Watson
A: 

I see 3 options:

  1. Do system("ls > tempfile") and then read the output of ls from tempfile
  2. Open a pipe using pipe(), then use fork() to spawn a new process. In child process close file descriptor 2 and substitute writing end of pipe for it by calling dup(). Next, call exec("ls",...) in child process. Finally read the output of ls from the pipe in parent process
  3. Do not use ls for enumerating files in current directory. There are functions to do that right in your program, i.e. opendir(),readdir(),closedir.

I highly recommend option 3.

pajton
A: 

Either use the approiate c calls to read the files in the directory or you generalize your program so it takes a list of files as input and take that list from ls piped to your program

> ls | yoursoftware
Harald Scheirich
+4  A: 

A simple way to do this is to use the boost filesystem directory iterator. Or just use opendir/readdir/closedir as others have suggested. There is no real upside to the way you are headed.

Code example, print sizes for regular files in a specified directory:

// $ g++ listdir.cc -o listdir -lboost_filesystem && ./listdir
#include <iostream>
#include <boost/filesystem.hpp>

int main(int argc, char* argv[]) {
  using std::cout;
  using namespace boost::filesystem;

  std::string path(argc <= 1 ? "." : argv[1]);

  if (is_directory(path)) {
    for (directory_iterator itr(path); itr!=directory_iterator(); ++itr) {
      cout << itr->path().filename() << ' '; // display filename only
      if (is_regular_file(itr->status())) // display size for regular files
        cout << " [" << file_size(itr->path()) << ']';
      cout << '\n';
    }
  }
  else cout << (exists(path) ? "Found: " : "Not found: ") << path << '\n';
}
Duck
A: 

Option 4. Use popen();

Steve
A: 

The consensus seems to be not to use "ls". However, for anyone that is interested in a function to do this:

/**
 * Execute a command and get the result.
 *
 * @param   cmd - The system command to run.
 * @return  The string command line output of the command.
 */
string GetStdoutFromCommand(string cmd) {

    string data;
    FILE * stream;
    const int max_buffer = 256;
    char buffer[max_buffer];
    cmd.append(" 2>&1"); // Do we want STDERR?

    stream = popen(cmd.c_str(), "r");
    if (stream) {
        while (!feof(stream))
            if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer);
        pclose(stream);
    }
    return data;
}
SuperJames