Como executar um comando e obter a saída do comando dentro do C ++ usando o POSIX?

Eu estou procurando uma maneira de obter a saída de um comando quando ele é executado de dentro de um programa C ++. Eu olhei para usar a function system (), mas isso apenas executará um comando. Aqui está um exemplo do que estou procurando:

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

Eu preciso executar um comando arbitrário e obter sua saída. Eu olhei para Boost.org, mas eu não encontrei nada que me dará o que eu preciso.

 #include  #include  #include  #include  #include  #include  std::string exec(const char* cmd) { std::array buffer; std::string result; std::shared_ptr 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; } 

Versão pré-C ++ 11:

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

Substitua popen e pclose por _popen e _pclose para Windows.

Obter stdout e stderr (e também escrever para stdin, não mostrado aqui) é fácil com meu header pstreams , que define classs iostream que funcionam como popen :

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

Eu usaria popen () (++ waqas) .

Mas às vezes você precisa ler e escrever …

Parece que ninguém faz as coisas da maneira mais difícil.

(Assumindo um ambiente Unix / Linux / Mac, ou talvez o Windows com uma camada de compatibilidade 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() )*/ } 

Você também pode querer brincar com leituras de seleção () e sem bloqueio.

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

Para Windows, o popen também funciona, mas abre a janela do console – que rapidamente pisca sobre o seu aplicativo de interface do usuário. Se você quer ser um profissional, é melhor desabilitar este “flashing” (Especialmente se o usuário final pode cancelá-lo).

Então aqui está a minha própria versão para o Windows:

(Esse código parcialmente recombinado de ideias escritas em amostras de código e 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 

Você pode usar a biblioteca Boost.Process . Não é oficialmente parte do impulso. Eu já vi isso funcionando bem para os outros. Infelizmente, o progresso do boost.process aparentemente foi interrompido. pstreams é outro projeto (aparentemente ativo). Certamente vale a pena tentar, eu diria – mas é apenas para sistemas de operação compatível com posix.

Duas abordagens possíveis.

1 / Eu não acho que popen() é parte do padrão C ++ (é parte do POSIX da memory), mas está disponível em todos os UNIX com os quais trabalhei (e você parece estar direcionando o UNIX desde que seu comando é ” ./some_command “).

2 / Na chance de que não exista popen() , você pode usar system( "./some_command >/tmp/some_command.out" ) ; em seguida, use as funções normais de E / S para processar o arquivo de saída.

Esta pode ser uma solução portátil. Segue os padrões.

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

Não consegui descobrir porque o popen / pclose está ausente do Codeblocks / MinGW. Então, resolvi o problema usando CreateProcess () e CreatePipe (). Aqui está a solução que funcionou para mim:

 //C++11 #include  #include  #include  #include  #include  #include  #include  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:%dn:%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:%dn:%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; }