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

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

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

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

8 ответов


#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::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
    if (!pipe) throw std::runtime_error("popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 128, 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 (!feof(pipe)) {
            if (fgets(buffer, 128, pipe) != NULL)
                result += buffer;
        }
    } catch (...) {
        pclose(pipe);
        throw;
    }
    pclose(pipe);
    return result;
}

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


получение как stdout, так и stderr (а также запись в stdin, не показанная здесь) легко peasy с my 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';
  // read child's stderr
  while (std::getline(proc.err(), line))
    std::cout << "stderr: " << line << '\n';
} 

Я хотел бы использовать 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);
}

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

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

(этот код частично рекомбинирован из идей, написанных в образцах codeproject и MSDN)

//
// 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;

    STARTUPINFO si = { sizeof(STARTUPINFO) };
    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 (50ms), 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

вы можете использовать библиотеку импульс.Процесс. Это официально не часть boost. Я видел, как это хорошо работает для других. К сожалению, boost.ход, видимо, был тупик. pstreams - еще один (по-видимому, активный) проект. Конечно, стоит попробовать, я бы сказал , но это только для совместимых с posix операционных систем.


два возможных подхода.

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

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


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

#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::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;
}

Я не мог понять, почему popen/pclose отсутствует в Codeblocks/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;
}