sintaxis programacion funciones ejecutar definicion compilar comandos codigos basicos c++ process posix

funciones - programacion c++ pdf



¿Cómo ejecutar un comando y obtener una salida de comando dentro de C++ usando POSIX? (9)

Estoy buscando una manera de obtener la salida de un comando cuando se ejecuta desde un programa C ++. He observado el uso de la función system (), 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 mirado Boost.org pero no he encontrado nada que me dé lo que necesito.


Dos enfoques posibles.

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

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


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


No pude averiguar por qué falta Popen / pclose en Codeblocks / MinGW. Así que resolví 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; }


Obtener tanto stdout como stderr (y también escribir en stdin, que no se muestra aquí) es fácil con mi encabezado pstreams , que define clases de 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''; // read child''s stderr while (std::getline(proc.err(), line)) std::cout << "stderr: " << line << ''/n''; }


Para Windows, popen también funciona, pero abre la ventana de la consola, que parpadea rápidamente sobre su aplicación de interfaz de usuario. 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 se recombina parcialmente a partir de ideas escritas en proyectos de código y muestras de 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


Puede utilizar la biblioteca Boost.Process . Aunque no es oficialmente parte del impulso. Lo he visto funcionar muy bien para otros. Desafortunadamente, el progreso del proceso de impulso aparentemente se ha detenido. pstreams es otro proyecto (aparentemente activo). Diría que vale la pena intentarlo, pero es solo para sistemas operativos compatibles con posix.


Suponiendo POSIX, código simple para capturar la salida estándar:

#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 del código son bienvenidas para una mayor funcionalidad:

https://github.com/ericcurtin/execxx


Yo uso popen () (++ waqas) .

Pero a veces necesitas leer y escribir ...

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

(Suponiendo un entorno Unix / Linux / Mac, o quizás 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 las lecturas de selección () y 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); }


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

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

Reemplace popen y pclose con _popen y _pclose para Windows.