Получение стандартного вывода из выполненного приложения - PullRequest
6 голосов
/ 08 декабря 2011

Кто-нибудь знает, как перехватить вывод (я думаю, его стандартный вывод) из execvp вместо того, чтобы система печатала его (в c на linux) в терминале?

Ответы [ 5 ]

6 голосов
/ 08 декабря 2011

execvp заменяет текущий запущенный процесс в памяти . Там нет "ловить" вывод.

Я подозреваю, что вы пытаетесь запустить внешний процесс из существующего процесса и проанализировать его вывод. Для этого вам нужно использовать popen(), который делает fork(), затем exec(), возвращая FILE * для чтения (что будет stdout процесса, который вы только что запустили).

4 голосов
/ 08 декабря 2011

Я не доверяю popen/pclose, так как я работал на слишком многих системах, где SIGCHLD обрабатывался немного по-другому. И я не доверяю синтаксическому анализу sh, используемому popen, поскольку я редко его использую.

Короткая 22-летняя книга О'Рейли Использование C в системе UNIX Дэйва Карри по-прежнему очень хороший справочник для такого рода вещей.

В любом случае, вот код. Он немного длинен, так как он разбирает пример строки "/bin/ls /etc" в массив {"/bin/ls", "/etc", 0}. Но я считаю, что использовать формат строки проще и короче 98% времени, хотя этот пример опровергает это.

Этот код генерирует список /etc. Вам нужно изменить некоторые вещи, например, например. NUMBER(), что совпадает с XtNumber(). И вам нужно решить, соответствует ли оно вашей обработке SIGCHLD.

int main(void) {  // list the files in /etc
   char buf[100];
   FILE *fp;
   int pid = spawnfp("/bin/ls /etc", &fp);
   while (fgets(buf, sizeof buf, fp))
      printf("%s", buf);
   fclose(fp);                    // pclose() replacement
   kill(pid, SIGKILL);            // pclose() replacement
   return 0;
}

Подпрограммы здесь:

static int spawnpipe(const char *argv[], int *fd) // popen() replacement
{
   int pid;
   int pipe_fds[2];

   if (pipe(pipe_fds) < 0)
      FatalError("pipe");

   switch ((pid = fork()))
   {
      case -1:
         FatalError("fork");
      case 0:                     // child
         close(1);
         close(2);
         dup(pipe_fds[0]);
         dup(pipe_fds[1]);
         close(pipe_fds[0]);
         close(pipe_fds[1]);

         execv(argv[0], (char * const *)argv);
         perror("execv");
         _exit(EXIT_FAILURE);    // sic, not exit()
      default:
         *fd = pipe_fds[0];
         close(pipe_fds[1]);
         return pid;
   }
}

Это преобразует строку ascii в список argv, который, вероятно, бесполезен для вас:

Bool convertStringToArgvList(char *p, const char **argv, int maxNumArgs)
{
   // Break up a string into tokens, on spaces, except that quoted bits, 
   // with single-quotes, are kept together, without the quotes. Such  
   // single-quotes cannot be escaped. A double-quote is just an ordinary char.
   // This is a *very* basic parsing, but ok for pre-programmed strings.
   int cnt = 0;
   while (*p)
   {
      while (*p && *p <= ' ')    // skip spaces
         p++;
      if (*p == '\'')            // single-quote block
      {
         if (cnt < maxNumArgs)
            argv[cnt++] = ++p;   // drop quote
         while (*p && *p != '\'')
            p++;
      }
      else if (*p)               // simple space-delineated token
      {
         if (cnt < maxNumArgs)
            argv[cnt++] = p;
         while (*p > ' ')
            p++;
      }
      if (*p)
         *p++ = 0;               // nul-terminate
   }
   if (cnt < maxNumArgs)
      argv[cnt++] = 0;
   return cnt <= maxNumArgs;     // check for too many tokens (unlikely)
}

Это преобразует строку аргумента в токены и, что более важно, fd в fp, так как OP запросил stdout:

int spawnfp(const char *command, FILE **fpp)
{
   const char *argv[100];
   int fd, pid;
   if (!convertStringToArgvList(strdupa(command), argv, NUMBER(argv)))
      FatalError("spawnfp");
   pid = spawnpipe(argv, &fd);
   *fpp = fdopen(fd, "r");
   return pid;
}
1 голос
/ 08 декабря 2011

Как уже говорили другие, popen - это то, что вы хотите использовать.Как то так ...

#include <iomanip>
#include <iostream>

using namespace std;

const int MAX_BUFFER = 255;

int main()
{
        string cmd;
        cout << "enter cmd: ";
        cin >> cmd;
        cout << endl << "running " << cmd << "…" << endl;


        string stdout;
        char buffer[MAX_BUFFER];
        FILE *stream = popen(cmd.c_str(), "r");
        while ( fgets(buffer, MAX_BUFFER, stream) != NULL )
        stdout.append(buffer);
        pclose(stream);


        cout << endl << "output: " << endl << stdout << endl;
}
1 голос
/ 08 декабря 2011

См. Документацию popen, думаю, это именно то, что вам нужно.

0 голосов
/ 22 апреля 2017

Я нашел этот ответ, который дает popen стиль интерфейса execvp.

https://codereview.stackexchange.com/questions/31063/popen-with-array-of-arguments

...