Как мне выполнить команду и получить вывод команды в C ++ с помощью POSIX?


463

Я ищу способ получить вывод команды, когда она запускается из программы на C ++. Я смотрел на использование system()функции, но это будет просто выполнить команду. Вот пример того, что я ищу:

std::string result = system("./some_command");

Мне нужно выполнить произвольную команду и получить ее вывод. Я посмотрел на boost.org , но я не нашел ничего, что дало бы мне то, что мне нужно.


Также см. Ответы в этом вопросе: /programming/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-cдля расширения отличного ответа ниже, который предоставляет методы для получения return codeи, stderrа также того, stdoutчто этот ответ уже объясняет
code_fodder

2
@code_fodder вы можете создать ссылку на stackoverflow.com/questions/52164723/…
Джонас Стейн

Ответы:


600
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

Версия до C ++ 11:

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>

std::string exec(const char* cmd) {
    char buffer[128];
    std::string result = "";
    FILE* pipe = popen(cmd, "r");
    if (!pipe) throw std::runtime_error("popen() failed!");
    try {
        while (fgets(buffer, sizeof buffer, pipe) != NULL) {
            result += buffer;
        }
    } catch (...) {
        pclose(pipe);
        throw;
    }
    pclose(pipe);
    return result;
}

Заменить popenи pcloseс _popenи _pcloseдля Windows.


70
Имейте в виду, что это будет захватывать только stdout, а не stderr .
kalaxy

14
Также помните, что может возникнуть исключение result += buffer, поэтому труба может быть закрыта неправильно.
Фред Фу

6
@Yasky: Когда выполняется программа int main(){ puts("ERROR"); }.
Dreamlax

8
Ответ хороший, но было бы лучше, если вы замените 'char * cmd' на 'const char * cmd'
fnc12

29
unique_ptr лучше подходит здесь, где фактический счетчик ссылок никогда не используется.
Чипперц

77

Получение как stdout, так и stderr (а также запись в stdin, здесь не показано) очень просто с моим заголовком pstreams , который определяет классы iostream, которые работают следующим образом popen:

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

int main()
{
  // run a process and create a streambuf that reads its stdout and stderr
  redi::ipstream proc("./some_command", redi::pstreams::pstdout | redi::pstreams::pstderr);
  std::string line;
  // read child's stdout
  while (std::getline(proc.out(), line))
    std::cout << "stdout: " << line << '\n';
  # if reading stdout stopped at EOF then reset the state:
  if (proc.eof() && proc.fail())
    proc.clear();
  // read child's stderr
  while (std::getline(proc.err(), line))
    std::cout << "stderr: " << line << '\n';
} 

18
Я не согласен. popenтребует использования API-интерфейса C stdio, я предпочитаю API iostreams. popenтребует, чтобы вы вручную FILEочистили ручку, pstreams сделает это автоматически. popenпринимает только const char*аргумент a , который требует осторожности, чтобы избежать атак внедрения оболочки; pstreams позволяет вам передавать вектор строк, похожих на execv, что безопаснее. popenничего не дает, кроме канала, pstreams сообщает PID ребенка, позволяющий вам отправлять сигналы, например, чтобы убить его, если он заблокирован или не выходит из него. Все это преимущества, даже если вы хотите только однонаправленный ввод-вывод.
Джонатан Уэйкли

1
Другая проблема, связанная с этим решением, заключается в том, что дочерний процесс пишет в stderr достаточно для заполнения буферов и блокировки до того, как он начнет запись в стандартный вывод. Родитель заблокирует чтение stdout, в то время как дочерний будет заблокирован, ожидая чтения stderr. ресурс тупик! По крайней мере, один из этих циклов будет лучше, чем асинхронный (то есть, с резьбой).
Джесси Чисхолм

1
@JesseChisholm, да, это может быть проблемой. Но вам не нужно использовать потоки, потому что pstreams позволяет приблизить неблокирующий ввод-вывод, используя интерфейс iostream, в частности, используя функцию readsome , которая проверяет готовность pstreambuf::in_avail(), поэтому блокировать не будет. Это позволяет демультиплексировать на stdout и stderr процесса, поскольку у каждого есть доступные данные. pstreambuf::in_avail()надежно работает только на 100%, если ОС поддерживает нестандартный FIONREAD ioctl, но поддерживается (по крайней мере) в GNU / Linux и Solaris.
Джонатан Уэйкли

13
@chiliNUT новая версия 1.0.1 использует лицензию Boost.
Джонатан

1
@JonathanWakely, как я могу убить ipstream после 5 секунд ожидания?
AK

34

Я бы использовал popen () (++ waqas) .

Но иногда вам нужно читать и писать ...

Кажется, что никто не делает вещи трудным путем больше.

(Предполагая среду Unix / Linux / Mac или Windows с уровнем совместимости POSIX ...)

enum PIPE_FILE_DESCRIPTERS
{
  READ_FD  = 0,
  WRITE_FD = 1
};

enum CONSTANTS
{
  BUFFER_SIZE = 100
};

int
main()
{
  int       parentToChild[2];
  int       childToParent[2];
  pid_t     pid;
  string    dataReadFromChild;
  char      buffer[BUFFER_SIZE + 1];
  ssize_t   readResult;
  int       status;

  ASSERT_IS(0, pipe(parentToChild));
  ASSERT_IS(0, pipe(childToParent));

  switch (pid = fork())
  {
    case -1:
      FAIL("Fork failed");
      exit(-1);

    case 0: /* Child */
      ASSERT_NOT(-1, dup2(parentToChild[READ_FD], STDIN_FILENO));
      ASSERT_NOT(-1, dup2(childToParent[WRITE_FD], STDOUT_FILENO));
      ASSERT_NOT(-1, dup2(childToParent[WRITE_FD], STDERR_FILENO));
      ASSERT_IS(0, close(parentToChild [WRITE_FD]));
      ASSERT_IS(0, close(childToParent [READ_FD]));

      /*     file, arg0, arg1,  arg2 */
      execlp("ls", "ls", "-al", "--color");

      FAIL("This line should never be reached!!!");
      exit(-1);

    default: /* Parent */
      cout << "Child " << pid << " process running..." << endl;

      ASSERT_IS(0, close(parentToChild [READ_FD]));
      ASSERT_IS(0, close(childToParent [WRITE_FD]));

      while (true)
      {
        switch (readResult = read(childToParent[READ_FD],
                                  buffer, BUFFER_SIZE))
        {
          case 0: /* End-of-File, or non-blocking read. */
            cout << "End of file reached..."         << endl
                 << "Data received was ("
                 << dataReadFromChild.size() << "): " << endl
                 << dataReadFromChild                << endl;

            ASSERT_IS(pid, waitpid(pid, & status, 0));

            cout << endl
                 << "Child exit staus is:  " << WEXITSTATUS(status) << endl
                 << endl;

            exit(0);


          case -1:
            if ((errno == EINTR) || (errno == EAGAIN))
            {
              errno = 0;
              break;
            }
            else
            {
              FAIL("read() failed");
              exit(-1);
            }

          default:
            dataReadFromChild . append(buffer, readResult);
            break;
        }
      } /* while (true) */
  } /* switch (pid = fork())*/
}

Вы также можете поиграть с select () и неблокирующим чтением.

fd_set          readfds;
struct timeval  timeout;

timeout.tv_sec  = 0;    /* Seconds */
timeout.tv_usec = 1000; /* Microseconds */

FD_ZERO(&readfds);
FD_SET(childToParent[READ_FD], &readfds);

switch (select (1 + childToParent[READ_FD], &readfds, (fd_set*)NULL, (fd_set*)NULL, & timeout))
{
  case 0: /* Timeout expired */
    break;

  case -1:
    if ((errno == EINTR) || (errno == EAGAIN))
    {
      errno = 0;
      break;
    }
    else
    {
      FAIL("Select() Failed");
      exit(-1);
    }

  case 1:  /* We have input */
    readResult = read(childToParent[READ_FD], buffer, BUFFER_SIZE);
    // However you want to handle it...
    break;

  default:
    FAIL("How did we see input on more than one file descriptor?");
    exit(-1);
}

1
Трудный путь правильный :) Мне нравится идея вызова select (), хотя в этом случае мне действительно нужно подождать, пока задача не завершится. Я оставлю этот код для другого моего проекта :)
Миша М

4
... или вы можете использовать существующую функцию posix_spawnp
Per Johansson

5
Ваш execlpвызов содержит ошибку: последний argпереданный указатель должен быть (char *) NULLдля правильного завершения списка аргументов с переменным числом аргументов (см. execlp(3)Ссылку).
Кристоф Марусси

Будет ли это работать на Unix, Linux и Windows? Можете ли вы, пожалуйста, заголовочные файлы?
Китту

Где вы передаете файл .bat в коде?
Китту

33

Для Windows popenтакже работает, но открывает консольное окно, которое быстро вспыхивает над вашим пользовательским интерфейсом. Если вы хотите быть профессионалом, лучше отключить эту «перепрошивку» (особенно, если конечный пользователь может отменить ее).

Итак, вот моя собственная версия для Windows:

(Этот код частично объединен с идеями, написанными в проектах The Code Project и MSDN.)

#include <windows.h>
#include <atlstr.h>
//
// Execute a command and get the results. (Only standard output)
//
CStringA ExecCmd(
    const wchar_t* cmd              // [in] command to execute
)
{
    CStringA strResult;
    HANDLE hPipeRead, hPipeWrite;

    SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
    saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
    saAttr.lpSecurityDescriptor = NULL;

    // Create a pipe to get results from child's stdout.
    if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0))
        return strResult;

    STARTUPINFOW si = {sizeof(STARTUPINFOW)};
    si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdOutput  = hPipeWrite;
    si.hStdError   = hPipeWrite;
    si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing.
                              // Requires STARTF_USESHOWWINDOW in dwFlags.

    PROCESS_INFORMATION pi = { 0 };

    BOOL fSuccess = CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
    if (! fSuccess)
    {
        CloseHandle(hPipeWrite);
        CloseHandle(hPipeRead);
        return strResult;
    }

    bool bProcessEnded = false;
    for (; !bProcessEnded ;)
    {
        // Give some timeslice (50 ms), so we won't waste 100% CPU.
        bProcessEnded = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0;

        // Even if process exited - we continue reading, if
        // there is some data available over pipe.
        for (;;)
        {
            char buf[1024];
            DWORD dwRead = 0;
            DWORD dwAvail = 0;

            if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
                break;

            if (!dwAvail) // No data available, return
                break;

            if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
                // Error, the child process might ended
                break;

            buf[dwRead] = 0;
            strResult += buf;
        }
    } //for

    CloseHandle(hPipeWrite);
    CloseHandle(hPipeRead);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return strResult;
} //ExecCmd

1
Это мое любимое решение для Windows, надеюсь, вы простите мои изменения. Я бы предложил сделать const-cast более явным, тогда как я рассматриваю явное использование wchar_tи CreateProcessWкак ненужное ограничение.
Волк

Видите ли вы какие-либо проблемы или потенциальные проблемы с этим составом? Я предпочитаю держать код как минимум и не писать его без необходимости.
TarmoPikaro

4
После прочтения функции CreateProcess (Windows) я вижу реальную опасность в этом: The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.поэтому, может быть, лучше сначала скопировать командную строку в отдельный буфер, чтобы предотвратить изменение исходных данных вызывающей стороны.
Волк

Этот ответ не обрабатывает stderr должным образом.
Рафаэль Шейнкер

Это также работает для систем Unix? Или я должен был бы использовать что-то еще для устройства Unix?
255.tar.xz

17

Два возможных подхода:

  1. Я не думаю, что это popen()является частью стандарта C ++ (это часть POSIX из памяти), но он доступен на каждом UNIX, с которым я работал (и вы, похоже, ориентируетесь на UNIX, так как ваша команда ./some_command).

  2. В случае отсутствия popen(), вы можете использовать system("./some_command >/tmp/some_command.out");, а затем использовать обычные функции ввода-вывода для обработки выходного файла.


Спасибо за popen, я собираюсь использовать это сейчас, и я буду беспокоиться о не POSIX системах, если это произойдет.
Миша М

8

Следующее может быть портативным решением. Это соответствует стандартам.

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <sstream>

std::string ssystem (const char *command) {
    char tmpname [L_tmpnam];
    std::tmpnam ( tmpname );
    std::string scommand = command;
    std::string cmd = scommand + " >> " + tmpname;
    std::system(cmd.c_str());
    std::ifstream file(tmpname, std::ios::in | std::ios::binary );
    std::string result;
    if (file) {
        while (!file.eof()) result.push_back(file.get())
            ;
        file.close();
    }
    remove(tmpname);
    return result;
}

// For Cygwin

int main(int argc, char *argv[])
{
    std::string bash = "FILETWO=/cygdrive/c/*\nfor f in $FILETWO\ndo\necho \"$f\"\ndone ";
    std::string in;
    std::string s = ssystem(bash.c_str());
    std::istringstream iss(s);
    std::string line;
    while (std::getline(iss, line))
    {
        std::cout << "LINE-> " + line + "  length: " << line.length() << std::endl;
    }
    std::cin >> in;
    return 0;
}

4
Я получаю это предупреждение с помощью gcc: «предупреждение: использование tmpnamопасно, лучше использовать mkstemp»
Марк Лаката

8

Я не мог понять, почему popen / pclose отсутствует в Code :: Blocks / MinGW. Поэтому я решил проблему, используя взамен CreateProcess () и CreatePipe ().

Вот решение, которое сработало для меня:

//C++11
#include <cstdio>
#include <iostream>
#include <windows.h>
#include <cstdint>
#include <deque>
#include <string>
#include <thread>

using namespace std;

int SystemCapture(
    string         CmdLine,    //Command Line
    string         CmdRunDir,  //set to '.' for current directory
    string&        ListStdOut, //Return List of StdOut
    string&        ListStdErr, //Return List of StdErr
    uint32_t&      RetCode)    //Return Exit Code
{
    int                  Success;
    SECURITY_ATTRIBUTES  security_attributes;
    HANDLE               stdout_rd = INVALID_HANDLE_VALUE;
    HANDLE               stdout_wr = INVALID_HANDLE_VALUE;
    HANDLE               stderr_rd = INVALID_HANDLE_VALUE;
    HANDLE               stderr_wr = INVALID_HANDLE_VALUE;
    PROCESS_INFORMATION  process_info;
    STARTUPINFO          startup_info;
    thread               stdout_thread;
    thread               stderr_thread;

    security_attributes.nLength              = sizeof(SECURITY_ATTRIBUTES);
    security_attributes.bInheritHandle       = TRUE;
    security_attributes.lpSecurityDescriptor = nullptr;

    if (!CreatePipe(&stdout_rd, &stdout_wr, &security_attributes, 0) ||
            !SetHandleInformation(stdout_rd, HANDLE_FLAG_INHERIT, 0)) {
        return -1;
    }

    if (!CreatePipe(&stderr_rd, &stderr_wr, &security_attributes, 0) ||
            !SetHandleInformation(stderr_rd, HANDLE_FLAG_INHERIT, 0)) {
        if (stdout_rd != INVALID_HANDLE_VALUE) CloseHandle(stdout_rd);
        if (stdout_wr != INVALID_HANDLE_VALUE) CloseHandle(stdout_wr);
        return -2;
    }

    ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&startup_info, sizeof(STARTUPINFO));

    startup_info.cb         = sizeof(STARTUPINFO);
    startup_info.hStdInput  = 0;
    startup_info.hStdOutput = stdout_wr;
    startup_info.hStdError  = stderr_wr;

    if(stdout_rd || stderr_rd)
        startup_info.dwFlags |= STARTF_USESTDHANDLES;

    // Make a copy because CreateProcess needs to modify string buffer
    char      CmdLineStr[MAX_PATH];
    strncpy(CmdLineStr, CmdLine.c_str(), MAX_PATH);
    CmdLineStr[MAX_PATH-1] = 0;

    Success = CreateProcess(
        nullptr,
        CmdLineStr,
        nullptr,
        nullptr,
        TRUE,
        0,
        nullptr,
        CmdRunDir.c_str(),
        &startup_info,
        &process_info
    );
    CloseHandle(stdout_wr);
    CloseHandle(stderr_wr);

    if(!Success) {
        CloseHandle(process_info.hProcess);
        CloseHandle(process_info.hThread);
        CloseHandle(stdout_rd);
        CloseHandle(stderr_rd);
        return -4;
    }
    else {
        CloseHandle(process_info.hThread);
    }

    if(stdout_rd) {
        stdout_thread=thread([&]() {
            DWORD  n;
            const size_t bufsize = 1000;
            char         buffer [bufsize];
            for(;;) {
                n = 0;
                int Success = ReadFile(
                    stdout_rd,
                    buffer,
                    (DWORD)bufsize,
                    &n,
                    nullptr
                );
                printf("STDERR: Success:%d n:%d\n", Success, (int)n);
                if(!Success || n == 0)
                    break;
                string s(buffer, n);
                printf("STDOUT:(%s)\n", s.c_str());
                ListStdOut += s;
            }
            printf("STDOUT:BREAK!\n");
        });
    }

    if(stderr_rd) {
        stderr_thread=thread([&]() {
            DWORD        n;
            const size_t bufsize = 1000;
            char         buffer [bufsize];
            for(;;) {
                n = 0;
                int Success = ReadFile(
                    stderr_rd,
                    buffer,
                    (DWORD)bufsize,
                    &n,
                    nullptr
                );
                printf("STDERR: Success:%d n:%d\n", Success, (int)n);
                if(!Success || n == 0)
                    break;
                string s(buffer, n);
                printf("STDERR:(%s)\n", s.c_str());
                ListStdOut += s;
            }
            printf("STDERR:BREAK!\n");
        });
    }

    WaitForSingleObject(process_info.hProcess,    INFINITE);
    if(!GetExitCodeProcess(process_info.hProcess, (DWORD*) &RetCode))
        RetCode = -1;

    CloseHandle(process_info.hProcess);

    if(stdout_thread.joinable())
        stdout_thread.join();

    if(stderr_thread.joinable())
        stderr_thread.join();

    CloseHandle(stdout_rd);
    CloseHandle(stderr_rd);

    return 0;
}

int main()
{
    int            rc;
    uint32_t       RetCode;
    string         ListStdOut;
    string         ListStdErr;

    cout << "STARTING.\n";

    rc = SystemCapture(
        "C:\\Windows\\System32\\ipconfig.exe",    //Command Line
        ".",                                     //CmdRunDir
        ListStdOut,                              //Return List of StdOut
        ListStdErr,                              //Return List of StdErr
        RetCode                                  //Return Exit Code
    );
    if (rc < 0) {
        cout << "ERROR: SystemCapture\n";
    }

    cout << "STDOUT:\n";
    cout << ListStdOut;

    cout << "STDERR:\n";
    cout << ListStdErr;

    cout << "Finished.\n";

    cout << "Press Enter to Continue";
    cin.ignore();

    return 0;
}

5
Спасибо! Это лучшая популярная реализация для Windows в Интернете! И, передав флаг CREATE_NO_WINDOW, можно, наконец, избавиться от надоедливых подсказок cmd, которые появляются.
Лачо Томов

1
Куда ты пропускаешь CREATE_NO_WINDOWчтоли?
Рафаэль Шейнкер

2
@ Билл Мур, если вы заметили, в вашем ответе есть ошибка. ListStdErrникогда не используется.
Рафаэль Шейнкер

4

Предполагая POSIX, простой код для захвата стандартного вывода:

#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <vector>

std::string qx(const std::vector<std::string>& args) {
  int stdout_fds[2];
  pipe(stdout_fds);

  int stderr_fds[2];
  pipe(stderr_fds);

  const pid_t pid = fork();
  if (!pid) {
    close(stdout_fds[0]);
    dup2(stdout_fds[1], 1);
    close(stdout_fds[1]);

    close(stderr_fds[0]);
    dup2(stderr_fds[1], 2);
    close(stderr_fds[1]);

    std::vector<char*> vc(args.size() + 1, 0);
    for (size_t i = 0; i < args.size(); ++i) {
      vc[i] = const_cast<char*>(args[i].c_str());
    }

    execvp(vc[0], &vc[0]);
    exit(0);
  }

  close(stdout_fds[1]);

  std::string out;
  const int buf_size = 4096;
  char buffer[buf_size];
  do {
    const ssize_t r = read(stdout_fds[0], buffer, buf_size);
    if (r > 0) {
      out.append(buffer, r);
    }
  } while (errno == EAGAIN || errno == EINTR);

  close(stdout_fds[0]);

  close(stderr_fds[1]);
  close(stderr_fds[0]);

  int r, status;
  do {
    r = waitpid(pid, &status, 0);
  } while (r == -1 && errno == EINTR);

  return out;
}

Вклады кода приветствуются для большей функциональности:

https://github.com/ericcurtin/execxx


2

Вы можете получить результат после запуска сценария с использованием канала. Мы используем каналы, когда хотим получить выход дочернего процесса.

int my_func() {
    char ch;
    FILE *fpipe;
    FILE *copy_fp;
    FILE *tmp;
    char *command = (char *)"/usr/bin/my_script my_arg";
    copy_fp = fopen("/tmp/output_file_path", "w");
    fpipe = (FILE *)popen(command, "r");
    if (fpipe) {
        while ((ch = fgetc(fpipe)) != EOF) {
            fputc(ch, copy_fp);
        }
    }
    else {
        if (copy_fp) {
            fprintf(copy_fp, "Sorry there was an error opening the file");
        }
    }
    pclose(fpipe);
    fclose(copy_fp);
    return 0;
}

Итак, вот сценарий, который вы хотите запустить. Поместите его в переменную команды с аргументами, которые принимает ваш скрипт (ничего, если нет аргументов). И файл, в который вы хотите захватить вывод скрипта, поместите его в copy_fp.

Поэтому popen запускает ваш скрипт и помещает вывод в fpipe, а затем вы можете просто скопировать все из этого в ваш выходной файл.

Таким образом, вы можете захватить выходные данные дочерних процессов.

И еще один процесс, вы можете напрямую поставить > оператор только в команде. Поэтому, если мы поместим все в файл во время выполнения команды, вам не придется ничего копировать.

В этом случае нет необходимости использовать трубы. Вы можете использовать just system, и он запустит команду и поместит вывод в этот файл.

int my_func(){
    char *command = (char *)"/usr/bin/my_script my_arg > /tmp/my_putput_file";
    system(command);
    printf("everything saved in my_output_file");
    return 0;
}

Вы можете прочитать YoLinux Tutorial: Fork, Exec и Process control для получения дополнительной информации.


1

Обратите внимание, что вы можете получить вывод, перенаправив вывод в файл и затем прочитав его

Это было показано в документации std::system

Вы можете получить код выхода, вызвав WEXITSTATUSмакрос.

    int status = std::system("ls -l >test.txt"); // execute the UNIX command "ls -l >test.txt"
    std::cout << std::ifstream("test.txt").rdbuf();
    std::cout << "Exit code: " << WEXITSTATUS(status) << std::endl;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.