Как перенаправить вывод системного вызова внутрь программы на C / C ++? - PullRequest
5 голосов
/ 16 апреля 2010

Я пишу программу на C ++, которая выполняет специальную обработку для всех файлов в текущем каталоге в ОС Linux.

Так что я думал об использовании системных вызовов, таких как system("ls"), чтобы получить список всех файлов.

а как его потом хранить внутри моей программы? (как перенаправить вывод команды ls, скажем, на строку, которую я объявил в программе)

Спасибо

Ответы [ 10 ]

14 голосов
/ 26 августа 2010

Похоже, консенсус заключается в том, чтобы не использовать "ls". Тем не менее, для тех, кто заинтересован в функции, чтобы сделать это:

/**
 * 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;
}
10 голосов
/ 16 апреля 2010

Я не думаю, что вы можете использовать систему для чтения вывода.

Попробуйте использовать popen.

   #include <stdio.h>

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

   int pclose(FILE *stream);

Но вы, вероятно, не хотите делать это для ls. Две причины:

  • Если вы хотите получить список каталогов, используйте файловую систему api напрямую (readdir) вместо использования команд оболочки и ее синтаксического анализа.
  • Кто-то указал мне на сообщение в блоге , в котором недавно объяснялось, почему анализ ls является плохой идеей.
7 голосов
/ 16 апреля 2010

Я предлагаю вам не вызывать ls - выполните работу правильно, используя opendir/readdir/closedir для непосредственного чтения каталога.

Пример кода, записи каталога печати:

// $ 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;
}
4 голосов
/ 16 апреля 2010

Простой способ сделать это - использовать итератор каталогов boost для файловой системы . Или просто используйте opendir / readdir / closedir, как предлагали другие. В том, как вы движетесь, нет ничего хорошего.

Пример кода, размеры печати для обычных файлов в указанном каталоге:

// $ 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';
}
1 голос
/ 26 августа 2010

Вариант 4. Используйте popen ();

0 голосов
/ 18 мая 2012

Использование системных вызовов fork () / exec () и pipe () представляется наилучшим общим методом в Unix.Это позволяет легче всего разделить стандартный поток вывода и стандартные ошибки вызываемой программы.Можно также ждать порожденного процесса и пожинать его, собирая его статус выхода.Наконец, при необходимости можно использовать неблокирующий ввод / вывод для чтения из вывода / ошибки вызванной программы.

Самая большая проблема заключается в том, что для достижения чего-то подобного в Windows вам, возможно, потребуется написать несколькосто строк кода.Я не нашел лучшего способа и не изучал проблему в контексте Windows.

http://support.microsoft.com/kb/190351

0 голосов
/ 04 августа 2011

Я думаю, что № 2 от Пейтон - самый подходящий ответ на вопрос.

Возможно, «ls» - не лучший пример команды, для которой вы хотели бы ее использовать, поскольку для этого есть другие функции библиотеки C, но есть другие программы, которые генерируют выходные данные, которые вам могут понадобиться. обрабатывать немедленно, не выполняя запись / чтение в файл.

# 1 также хорош в системе типов UNIX / Linux, которая реализует эффективную файловую систему RAM, которая может использоваться для чтения / записи глобальных данных системы. Практически во всех системах это действительно хороший «быстрый» и «грязный» способ выполнить работу.

Опять же, большинство ответов предлагают лучшие способы получения содержимого каталога с использованием собственных библиотек C, но есть случаи, когда такие функции, как pipe (), fork (), dup (), exec (), system ( ) и popen () подходят для связи с системными процессами.

0 голосов
/ 16 апреля 2010

Либо используйте соответствующие вызовы c для чтения файлов в каталоге, либо вы обобщаете свою программу, чтобы она принимала список файлов в качестве входных данных, и переносите этот список из ls, переданного в вашу программу

> ls | yoursoftware
0 голосов
/ 16 апреля 2010

вижу 3 варианта:

  1. Выполните system("ls > tempfile") и затем прочитайте вывод ls из временного файла
  2. Откройте канал, используя pipe(), затем используйте fork(), чтобы создать новый процесс. В дочернем процессе закройте файловый дескриптор 2 и замените записывающий конец конвейера на него, вызвав dup(). Затем вызовите exec("ls",...) в дочернем процессе. Наконец, прочитайте вывод ls из канала в родительском процессе
  3. Не используйте ls для перечисления файлов в текущем каталоге. Есть функции, которые делают это прямо в вашей программе, т.е. opendir(),readdir(),closedir.

Я настоятельно рекомендую вариант 3.

0 голосов
/ 16 апреля 2010

Вы в UNIX или Windows? В UNIX вы должны создать дочерний процесс с помощью вызова fork (), а затем создать несколько каналов и назначить STDOUT дочернего процесса на STDIN родительского процесса. В Windows обязательно должно быть что-то похожее в Win32 API.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...