Как использовать команды оболочки в Code :: blocks c ++? [Дубликат]

+ соответствует хотя бы одному символу

* соответствует любому числу (включая 0) символов

. ? указывает ленивое выражение, поэтому оно будет соответствовать как можно меньше символов.

344
задан jotik 10 April 2016 в 18:06
поделиться

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

Pre-C ++ 11 version:

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

450
ответ дан Al.G. 15 August 2018 в 20:02
поделиться
  • 1
    Имейте в виду, что это займет только stdout , а не stderr . – kalaxy 1 November 2011 в 01:53
  • 2
    Также имейте в виду, что исключение может возникать в result += buffer, поэтому труба может быть неправильно закрыта. – Fred Foo 19 May 2012 в 21:27
  • 3
    @Yasky: Когда исполняемая программа int main(){ puts("ERROR"); }. – dreamlax 16 December 2013 в 23:03
  • 4
    Ответ хороший, но было бы лучше, если бы вы заменили 'char * cmd' на 'const char * cmd' – fnc12 27 December 2014 в 15:20
  • 5
    unique_ptr лучше подходит здесь, где фактический счетчик ссылок никогда не используется. – Czipperz 29 May 2016 в 08:42

Я не мог понять, почему 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;
}
3
ответ дан Bill Moore 15 August 2018 в 20:02
поделиться
  • 1
    Спасибо! Это самая лучшая версия для Windows в Интернете! И, передав флаг CREATE_NO_WINDOW, вы можете, наконец, избавиться от раздражающих подсказок cmd, которые появляются. – Lacho Tomov 19 June 2018 в 18:05

Я бы использовал 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);
}
28
ответ дан Gilles 15 August 2018 в 20:02
поделиться
  • 1
    Трудный путь прав. Мне нравится идея с вызовом select (), хотя в этом случае мне действительно нужно подождать, пока задача не завершится. Я сохраню этот код для другого проекта, который у меня есть :) – Misha M 27 January 2009 в 03:21
  • 2
    ... или вы можете использовать существующую функцию posix_spawnp – Per Johansson 3 January 2013 в 17:18
  • 3
    У вашего вызова execlp есть ошибка: последний переданный указатель arg должен быть (char *) NULL для правильного завершения списка вариационных аргументов (см. execlp(3) для справки). – Kristóf Marussy 19 October 2013 в 19:38

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

14
ответ дан Johannes Schaub - litb 15 August 2018 в 20:02
поделиться

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

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

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

14
ответ дан paxdiablo 15 August 2018 в 20:02
поделиться
  • 1
    Спасибо за popen, я пока буду использовать это, и я буду беспокоиться о системах, отличных от POSIX, если это произойдет. – Misha M 27 January 2009 в 03:25

Получение как stdout, так и stderr (а также запись в stdin, не показано здесь) легко peasy с моим заголовком 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';
} 
58
ответ дан Riot 15 August 2018 в 20:02
поделиться
  • 1
    Я не согласен. popen требует использования C stdio API, я предпочитаю API iostreams. popen требует, чтобы вы вручную очистили ручку FILE, pstreams сделали это автоматически. popen принимает только аргумент const char*, который требует осторожности, чтобы избежать атак оболочки, pstreams позволяет вам передавать вектор строк, подобных execv, что является более безопасным. popen дает вам ничего, кроме трубы, pstreams сообщает вам PID ребенка, позволяющий отправлять сигналы, например. убить его, если он заблокирован или не выйдет. Все это преимущества, даже если вам нужен только однонаправленный ввод-вывод. – Jonathan Wakely 10 October 2012 в 18:08
  • 2
    но pstreams распространяется по лицензии GNU LGPL, которая немного ограничительна для коммерческого использования. [Д0] stackoverflow.com/questions/2145216/… – 0xC0DEGURU 16 January 2014 в 17:48
  • 3
    Хорошая библиотека, однако, LGPL убивает ее. Никто, кому нужна небольшая небольшая функциональность, будет связывать такую ​​функцию с динамической библиотекой, если она будет в коммерческом проекте. – tach 18 January 2014 в 02:33
  • 4
    @tach: вам не нужно связывать его как динамическую библиотеку. Там ничего нет, кроме файла заголовка. Чтобы подтвердить, что LGPL можно использовать только с библиотеками только для заголовков, посмотрите FAQ по собственной библиотеке . Имейте в виду, что это относится к LGPL2, поэтому могут возникнуть дополнительные осложнения с LGPL3. – pyrocrasty 27 February 2016 в 22:20
  • 5
    @chiliNUT в новой версии 1.0.1 используется лицензия Boost. – Jonathan Wakely 3 February 2017 в 14:50

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

#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;
}
4
ответ дан user 15 August 2018 в 20:02
поделиться
  • 1
    Я получаю это предупреждение с gcc: & quot; предупреждение: использование tmpnam опасно, лучше использовать mkstemp & quot; – Mark Lakata 28 August 2014 в 00:44

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

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

(Этот код частично рекомбинирован из идей, написанных в образцах кодекса и 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
21
ответ дан Wolf 15 August 2018 в 20:02
поделиться
  • 1
    Это мое любимое решение для Windows, надеюсь, вы простите мои изменения. Я бы предложил сделать const-cast более явным, тогда как я считаю явное использование wchar_t и CreateProcessW в качестве ненужного ограничения. – Wolf 16 May 2017 в 10:13
  • 2
    Вы видите какую-либо проблему или потенциальную проблему с этим актом? Я предпочитаю держать код как минимум и не писать его без необходимости. – TarmoPikaro 17 May 2017 в 16:27
  • 3
    После чтения функции 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. . Возможно, лучше сначала скопировать командную строку в отдельный буфер, чтобы не позволяет собеседнику изменить исходный вход. – Wolf 18 May 2017 в 12:29
Другие вопросы по тегам:

Похожие вопросы: