我正在寻找一种方法来获得命令的输出,当它从c++程序中运行时。我已经看到了使用system()函数,但它只会执行一个命令。以下是我正在寻找的一个例子:
std::string result = system("./some_command");
我需要运行任意命令并获得其输出。我在boost.org上找过了,但是我还没有找到任何能满足我需要的东西。
我正在寻找一种方法来获得命令的输出,当它从c++程序中运行时。我已经看到了使用system()函数,但它只会执行一个命令。以下是我正在寻找的一个例子:
std::string result = system("./some_command");
我需要运行任意命令并获得其输出。我在boost.org上找过了,但是我还没有找到任何能满足我需要的东西。
当前回答
waqas的答案的c++流实现:
#include <istream>
#include <streambuf>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <string>
class execbuf : public std::streambuf {
protected:
std::string output;
int_type underflow(int_type character) {
if (gptr() < egptr()) return traits_type::to_int_type(*gptr());
return traits_type::eof();
}
public:
execbuf(const char* command) {
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
this->output += buffer.data();
}
setg((char*)this->output.data(), (char*)this->output.data(), (char*)(this->output.data() + this->output.size()));
}
};
class exec : public std::istream {
protected:
execbuf buffer;
public:
exec(char* command) : std::istream(nullptr), buffer(command, fd) {
this->rdbuf(&buffer);
}
};
这段代码通过stdout捕获所有输出。如果你只想捕获stderr,那么像这样传递你的命令:
sh -c '<your-command>' 2>&1 > /dev/null
如果你想同时捕获stdout和stderr,那么命令应该是这样的:
sh -c '<your-command>' 2>&1
其他回答
waqas的答案的c++流实现:
#include <istream>
#include <streambuf>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <string>
class execbuf : public std::streambuf {
protected:
std::string output;
int_type underflow(int_type character) {
if (gptr() < egptr()) return traits_type::to_int_type(*gptr());
return traits_type::eof();
}
public:
execbuf(const char* command) {
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
this->output += buffer.data();
}
setg((char*)this->output.data(), (char*)this->output.data(), (char*)(this->output.data() + this->output.size()));
}
};
class exec : public std::istream {
protected:
execbuf buffer;
public:
exec(char* command) : std::istream(nullptr), buffer(command, fd) {
this->rdbuf(&buffer);
}
};
这段代码通过stdout捕获所有输出。如果你只想捕获stderr,那么像这样传递你的命令:
sh -c '<your-command>' 2>&1 > /dev/null
如果你想同时捕获stdout和stderr,那么命令应该是这样的:
sh -c '<your-command>' 2>&1
下面可能是一个可移植的解决方案。它遵循标准。
#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;
}
您可以在使用管道运行脚本后获得输出。当我们需要子进程的输出时,我们使用管道。
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;
}
这就是你要运行的脚本。将它与脚本所接受的参数一起放在命令变量中(如果没有参数,就什么都没有)。以及您想要捕获脚本输出的文件,将其放在copy_fp中。
popen运行你的脚本并将输出放到fpipe中然后你可以将所有内容复制到输出文件中。
通过这种方式,您可以捕获子流程的输出。
另一个过程是你可以直接把>运算符放在命令中。因此,如果我们在运行命令时将所有内容都放在一个文件中,则不需要复制任何内容。
在这种情况下,不需要使用管道。您可以只使用system,它将运行命令并将输出放在该文件中。
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;
}
你可以阅读YoLinux教程:Fork, Exec和进程控制了解更多信息。
两种可能的方法:
我不认为popen()是c++标准的一部分(从内存来看,它是POSIX的一部分),但它在我使用过的每个UNIX上都可用(您似乎针对的是UNIX,因为您的命令是./some_command)。 在不存在popen()的情况下,您可以使用system("。/some_command >/tmp/some_command.out");,然后使用正常的I/O函数处理输出文件。
对于Windows, popen也可以工作,但它会打开一个控制台窗口-它会在你的UI应用程序上快速闪烁。如果你想成为一个专业人士,最好禁用这种“闪烁”(特别是如果最终用户可以取消它)。
下面是我自己的Windows版本:
(此代码部分重组自代码项目和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