Як виконати команду і отримати вихід команди в межах 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/…
Йонас Штейн

Відповіді:


599
#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.


69
Майте на увазі, що це буде лише захоплювати stdout, а не твердіше .
kalaxy

14
Також пам’ятайте, що може статися виняток у result += buffer, тому труба може бути неправильно закрита.
Фред Фоо

6
@Yasky: Коли програма виконується int main(){ puts("ERROR"); }.
dreamlax

8
Відповідь хороша, але було б краще, якби ви замінили 'char * cmd' на 'const char * cmd'
fnc12

29
Тут краще підходить унікальний_ptr, де фактична кількість посилань ніколи не використовується.
Czipperz

77

Отримати як stdout, так і stderr (а також писати на stdin, не показані тут) - це просто "pey" з моїм заголовком 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 stdio C, я віддаю перевагу API API. popenвимагає, щоб ви вручну чистили FILEручку, плігери роблять це автоматично. popenприймає лише const char*аргумент, який вимагає обережності, щоб уникнути атак на ін'єкцію оболонки, pstreams дозволяє передавати вектор рядків, подібних до execv, що безпечніше. popenне дає нічого, крім труби, pstreams повідомляє вам PID дитини, що дозволяє вам надсилати сигнали, наприклад, вбивати його, якщо він заблокований або не виходить. Все це є перевагами, навіть якщо ви хочете лише однонаправленого вводу-виводу.
Джонатан Вейклі

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

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

13
@chiliNUT у новій версії 1.0.1 використовується ліцензія Boost.
Джонатан Вейклі

1
@JonathanWakely Як я можу вбити ipstream після того, як скажу 5-секундний тайм-аут?
АК

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 (), хоча в цьому випадку мені потрібно чекати, поки завдання виконається. Я збережу цей код для іншого проекту, який у мене є :)
Misha M

4
... або ви можете використовувати існуючу функцію posix_spawnp
Per Johansson

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

Чи буде це працювати на Unix, Linux та Windows? Чи можете ви також порадувати файли заголовків?
kittu

Куди ви передаєте .bat файл у коді?
kittu

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як непотрібне обмеження.
Вовк

Чи бачите ви якусь проблему чи потенційну проблему з цим акторським складом? Я вважаю за краще тримати код як мінімум і не писати його без потреби.
ТармоПікаро

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.тому, можливо, краще спершу скопіювати командний рядок в окремий буфер, щоб запобігти абоненту змінити вихідний вхід.
Вовк

Ця відповідь не обробляє більш жорстко.
Рефаел Шейнкер

Це працює також для систем 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

Я не міг зрозуміти, чому 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
@Bill Moore, якщо ви помітили, у вашій відповіді є помилка. ListStdErrніколи не використовується.
Refael Sheinker

7

Наступне може бути портативним рішенням. Це відповідає стандартам.

#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"
Марк Лаката

4

Припускаючи POSIX, простий код для зйомки stdout:

#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, і тоді ви можете просто скопіювати все з цього у вихідний файл.

Таким чином ви можете фіксувати результати дочірніх процесів.

І ще один процес - це ви можете безпосередньо поставити > оператора лише в команду. Тож якщо ми запустимо все у файл під час виконання команди, вам не доведеться нічого копіювати.

У цьому випадку не потрібно використовувати труби. Ви можете використовувати просто 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: Fork, Exec та управління процесами для отримання додаткової інформації.


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.