¿Cómo ejecuto un comando y obtengo la salida del comando dentro de C ++ usando POSIX?


463

Estoy buscando una manera de obtener la salida de un comando cuando se ejecuta desde un programa C ++. He visto el uso de la system()función, pero eso solo ejecutará un comando. Aquí hay un ejemplo de lo que estoy buscando:

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

Necesito ejecutar un comando arbitrario y obtener su salida. He visto boost.org , pero no he encontrado nada que me dé lo que necesito.


También ver las respuestas en esta pregunta: /programming/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-cpara una extensión de la gran respuesta siguiente que proporcione métodos para obtener el return codey stderrademás stdoutque esta respuesta ya explica
code_fodder

2
@code_fodder puede crear un enlace a stackoverflow.com/questions/52164723/…
Jonas Stein

Respuestas:


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

Versión anterior a 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;
}

Reemplazar popeny pclosecon _popeny _pclosepara Windows.


70
Tenga en cuenta que esto solo tomará stdout y no stderr .
kalaxy

14
También tenga en cuenta que puede ocurrir una excepción result += buffer, por lo que es posible que la tubería no se cierre correctamente.
Fred Foo

66
@Yasky: cuando se ejecuta el programa que se está ejecutando int main(){ puts("ERROR"); }.
dreamlax

8
La respuesta es buena, pero sería mejor si reemplaza 'char * cmd' con 'const char * cmd'
fnc12

29
unique_ptr se ajusta mejor aquí, donde el recuento de referencia real nunca se usa.
Czipperz

77

Obtener stdout y stderr (y también escribir en stdin, que no se muestra aquí) es fácil con mi encabezado pstreams , que define las clases iostream que funcionan como 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
Estoy en desacuerdo. popenrequiere que uses la API de C stdio, prefiero la API de iostreams. popenrequiere que limpie manualmente el FILEcontrolador, pstreams lo hace automáticamente. popensolo acepta un const char*argumento, que requiere cuidado para evitar ataques de inyección de shell, pstreams le permite pasar un vector de cadenas similar a execv, que es más seguro. popenno le da más que una tubería, pstreams le dice el PID del niño que le permite enviar señales, por ejemplo, matarlo si está bloqueado o no sale. Todas esas son ventajas, incluso si solo desea IO unidireccional.
Jonathan Wakely

1
Otro problema con esta solución es si el niño escribe en stderr lo suficiente como para llenar los búferes y bloquear antes de que comience a escribir en stdout. El padre bloqueará la lectura stdout, mientras que el niño está bloqueado esperando que se lea stderr. punto muerto de recursos! Al menos uno de esos bucles sería mejor como asíncrono (es decir, roscado).
Jesse Chisholm

1
@JesseChisholm, sí, eso podría ser un problema. Pero no es necesario utilizar subprocesos porque pstreams permite una aproximación de E / S sin bloqueo mediante la interfaz iostream, específicamente mediante la función readsome , que comprueba la disponibilidad utilizando pstreambuf::in_avail(), por lo que no se bloquea. Eso permite la demultiplexación en stdout y stderr del proceso, ya que cada uno tiene datos disponibles. pstreambuf::in_avail()solo funciona al 100% de manera confiable si el sistema operativo admite el ioctl no estándar de FIONREAD, pero eso es compatible con (al menos) GNU / Linux y Solaris.
Jonathan Wakely

13
@chiliNUT la nueva versión 1.0.1 utiliza la licencia Boost.
Jonathan Wakely

1
@ JonathanWakely ¿cómo puedo matar el ipstream después de un tiempo de espera de 5 segundos?
AK

34

Yo usaría popen () (++ waqas) .

Pero a veces necesitas leer y escribir ...

Parece que ya nadie hace las cosas de la manera difícil.

(Suponiendo un entorno Unix / Linux / Mac, o tal vez Windows con una capa de compatibilidad 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())*/
}

También es posible que desee jugar con select () y lecturas sin bloqueo.

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
El camino difícil es correcto :) Me gusta la idea con la llamada select (), aunque en este caso, realmente necesito esperar hasta que se complete la tarea. Guardaré este código para otro proyecto que tengo :)
Misha M

44
... o podría usar la función posix_spawnp existente
Por Johansson

55
Su execlpllamada tiene un error: el último argpuntero pasado debe ser (char *) NULLpara terminar correctamente la lista de argumentos variables (ver execlp(3)para referencia).
Kristóf Marussy

¿Funcionará esto en Unix, Linux y Windows? ¿Puedes por favor encabezar archivos también?
Kittu

¿Dónde está pasando el archivo .bat en el código?
Kittu

33

Para Windows, popentambién funciona, pero abre una ventana de consola, que parpadea rápidamente sobre su aplicación de IU. Si desea ser un profesional, es mejor deshabilitar este "parpadeo" (especialmente si el usuario final puede cancelarlo).

Así que aquí está mi propia versión para Windows:

(Este código está parcialmente recombinado a partir de ideas escritas en The Code Project y muestras de 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
Esta es mi solución favorita para Windows, espero que perdone mis cambios. Sugeriría hacer que el const-cast sea más explícito, mientras que considero el uso explícito de wchar_ty CreateProcessWcomo una restricción innecesaria.
Wolf

¿Ves algún problema o problema potencial con este elenco? Prefiero mantener el código al mínimo y no escribirlo sin necesidad.
TarmoPikaro

44
Después de leer la función CreateProcess (Windows) , veo un peligro real al hacer esto: 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.por lo tanto, tal vez sea mejor copiar la línea de comando en un búfer separado primero, para evitar que la persona que llama cambie su entrada original.
Wolf

Esta respuesta no maneja stderr correctamente.
Refael Sheinker

¿Esto también funciona para sistemas Unix? ¿O tendría que usar algo más para un dispositivo Unix?
255.tar.xz

17

Dos posibles enfoques:

  1. No creo que popen()sea ​​parte del estándar C ++ (es parte de POSIX de la memoria), pero está disponible en todos los UNIX con los que he trabajado (y parece que estás apuntando a UNIX ya que tu comando lo está ./some_command).

  2. En el caso de que no exista popen(), puede usar system("./some_command >/tmp/some_command.out");, luego use las funciones normales de E / S para procesar el archivo de salida.


Gracias por popen, voy a usar eso por ahora y me preocuparé por los sistemas que no son POSIX si eso surge.
Misha M

8

Lo siguiente podría ser una solución portátil. Sigue los estándares.

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

44
Recibo esta advertencia con gcc: "advertencia: el uso de tmpnames peligroso, mejor uso mkstemp"
Mark Lakata

8

No pude entender por qué falta popen / pclose en Code :: Blocks / MinGW. Así que solucioné el problema usando CreateProcess () y CreatePipe () en su lugar.

Aquí está la solución que funcionó para mí:

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

55
¡Gracias! ¡Esta es la mejor implementación de popen para Windows en Internet! Y al pasar el indicador CREATE_NO_WINDOW, uno finalmente puede deshacerse de las molestas indicaciones de cmd que aparecen.
Lacho Tomov

1
¿Dónde pasas la CREATE_NO_WINDOWcosita?
Refael Sheinker

2
@Bill Moore, si te das cuenta, hay un error en tu respuesta. ListStdErrnunca se usa
Refael Sheinker

4

Suponiendo POSIX, código simple para capturar 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;
}

Las contribuciones de código son bienvenidas para una mayor funcionalidad:

https://github.com/ericcurtin/execxx


2

Puede obtener el resultado después de ejecutar un script utilizando una tubería. Usamos tuberías cuando queremos la salida del proceso hijo.

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

Así que aquí está el script, que desea ejecutar. Póngalo en una variable de comando con los argumentos que toma su secuencia de comandos (nada si no hay argumentos). Y el archivo donde desea capturar la salida del script, póngalo en copy_fp.

Entonces, popen ejecuta su script y pone la salida en fpipe y luego puede copiar todo, desde eso a su archivo de salida.

De esta manera, puede capturar los resultados de los procesos secundarios.

Y otro proceso es que puedes poner directamente > operador solo en el comando. Entonces, si colocaremos todo en un archivo mientras ejecutamos el comando, no tendrá que copiar nada.

En ese caso, no hay necesidad de usar tuberías. Puede usar just system, y ejecutará el comando y colocará la salida en ese archivo.

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

Puede leer el Tutorial de YoLinux: Fork, Exec y Control de procesos para obtener más información.


1

Tenga en cuenta que puede obtener salida redirigiendo la salida al archivo y luego leyéndola

Se mostró en la documentación de std::system

Puede recibir el código de salida llamando a WEXITSTATUSmacro.

    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;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.