我正在寻找一种方法来获取从C++程序中运行命令的输出.我已经看过使用system()函数,但这只会执行一个命令.这是我正在寻找的一个例子:
std::string result = system("./some_command");
我需要运行一个任意命令并获取其输出.我看过Boost.org,但我找不到任何可以满足我需要的东西.
#include#include #include #include #include #include std::string exec(const char* cmd) { std::array buffer; std::string result; std::unique_ptr 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; }
Pre-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 (fgets(buffer, sizeof buffer, pipe) != NULL) { result += buffer; } } catch (...) { pclose(pipe); throw; } pclose(pipe); return result; }
替换popen
和pclose
用于_popen
和_pclose
Windows.
同时获取stdout和stderr(以及写入stdin,这里没有显示)很容易使用我的pstreams标头,它定义了iostream类,其工作方式如下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'; }
我会用popen() (++ waqas).
但有时你需要阅读和写作......
似乎没有人再以艰难的方式做事了.
(假设Unix/Linux/Mac环境,或者可能是带有POSIX兼容层的Windows ......)
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
也可以工作,但它会打开一个控制台窗口 - 它会快速闪烁在UI应用程序上.如果你想成为一名专业人士,最好禁用这种"闪烁"(特别是如果最终用户可以取消它).
所以这是我自己的Windows版本:
(此代码部分重新组合在代码项目和MSDN示例中编写的想法)
#include#include // // 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
两种可能的方法:
我不认为它popen()
是C++标准的一部分(它是来自内存的POSIX的一部分),但它可以在我使用的每个UNIX上使用(并且你似乎是因为你的命令而瞄准UNIX ./some_command
).
如果没有popen()
,则可以使用system("./some_command >/tmp/some_command.out");
,然后使用普通的I/O函数来处理输出文件.
以下可能是可移植的解决方案。它遵循标准。
#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::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; }