C ++プログラム内から実行したときにコマンドの出力を取得する方法を探しています。system()
関数の使用方法を確認しましたが、これはコマンドを実行するだけです。これが私が探しているものの例です:
std::string result = system("./some_command");
任意のコマンドを実行して、その出力を取得する必要があります。boost.orgを見てきましたが、必要なものを提供するものは何も見つかりませんでした。
C ++プログラム内から実行したときにコマンドの出力を取得する方法を探しています。system()
関数の使用方法を確認しましたが、これはコマンドを実行するだけです。これが私が探しているものの例です:
std::string result = system("./some_command");
任意のコマンドを実行して、その出力を取得する必要があります。boost.orgを見てきましたが、必要なものを提供するものは何も見つかりませんでした。
回答:
#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;
}
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;
}
交換するpopen
とpclose
して_popen
および_pclose
Windows用。
result += buffer
あるため、パイプが適切に閉じられない場合があることに注意してください。
int main(){ puts("ERROR"); }
。
stdoutとstderrの両方を取得する(また、ここには示されていないstdinに書き込む)のは、次のように機能するiostreamクラスを定義するpstreamsヘッダーを使用すると簡単です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';
}
popen
C stdio APIを使用する必要があります。私はiostreams APIを使用します。ハンドルpopen
を手動でクリーンアップする必要がありますがFILE
、pstreamは自動的にクリーンアップします。引数のpopen
aのみを受け入れconst char*
ます。これは、シェルインジェクション攻撃を回避するための注意が必要です。pstreamではexecv
、に似た文字列のベクトルを渡すことができるため、より安全です。popen
パイプのみを提供します。pstreamsは、子のPIDを通知して、ブロックされている場合や終了していない場合などに、シグナルを送信してシグナルを送信できるようにします。一方向のIOのみが必要な場合でも、これらすべての利点があります。
pstreambuf::in_avail()
。これにより、プロセスのstdoutとstderrが使用可能なデータを持っているので、それぞれで逆多重化できます。pstreambuf::in_avail()
OSが非標準のFIONREAD ioctlをサポートしている場合にのみ100%確実に動作しますが、これは(少なくとも)GNU / LinuxおよびSolarisでサポートされています。
私は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);
}
execlp
呼び出しはバグがあります。最後にarg
渡されたポインタでなければならない(char *) NULL
適切に(参照可変長引数リストを終了するためにexeclp(3)
参考のために)。
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
wchar_t
、およびの明示的な使用はCreateProcessW
不要な制限と見なします。
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.
伴います:したがって、最初にコマンドラインを別のバッファーにコピーして、呼び出し元が元の入力を変更されないようにする方が良いでしょう。
2つの可能なアプローチ:
私はpopen()
C ++標準の一部ではないと思います(メモリからのPOSIXの一部です)が、これまでに使用したすべてのUNIXで使用できます(コマンドがであるため、UNIXをターゲットにしているようです./some_command
)。
オフのチャンスには何があることをpopen()
、あなたが使用することはできませんsystem("./some_command >/tmp/some_command.out");
、その後、出力ファイルを処理するために、通常のI / O機能を使用しています。
なぜpopen / pcloseがCode :: Blocks / MinGWにないのか理解できませんでした。そこで、代わりにCreateProcess()とCreatePipe()を使用して問題を回避しました。
これが私のために働いた解決策です:
//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;
}
CREATE_NO_WINDOW
ますか?
ListStdErr
使用されません。
以下は、ポータブルなソリューションかもしれません。それは標準に従います。
#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;
}
tmpnam
は危険です。より適切に使用してくださいmkstemp
」
POSIXを想定して、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;
}
コードの貢献は、より多くの機能のために歓迎されています:
パイプを使用してスクリプトを実行した後、出力を取得できます。子プロセスの出力が必要な場合は、パイプを使用します。
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に入れ、そこからすべてを出力ファイルにコピーできます。
このようにして、子プロセスの出力をキャプチャできます。
もう1つのプロセスは、>
オペレーターを直接コマンドにのみ入れることができるというものです。したがって、コマンドの実行中にすべてをファイルに入れれば、何もコピーする必要はありません。
その場合、パイプを使用する必要はありません。だけを使用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;
}
あなたは読むことができフォーク、EXECおよびプロセス制御:YoLinuxチュートリアルの詳細については。
出力をファイルにリダイレクトして読み取ることで出力を取得できることに注意してください
それはのドキュメントに示されていました std::system
WEXITSTATUS
マクロを呼び出すことで終了コードを受け取ることができます。
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;
/programming/52164723/how-to-execute-a-command-and-get-return-code-stdout-and-stderr-of-command-in-c
それが取得するメソッドを提供して下の偉大な答えの延長return code
とstderr
同様にstdout
、この答えは、すでに説明していること