如何在 C++ 中执行命令并获取命令的 return 代码标准输出和标准错误
How to execute a command and get return code stdout and stderr of command in C++
给出以下答案(第一个 c++11 答案):
How do I execute a command and get the output of the command within C++ using POSIX?
为方便起见,下面是实现:
#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;
}
这非常适合执行命令(例如 std::string res = exec("ls");
)并将标准输出转换为字符串。
但它不做的是获取命令 return 代码(pass/fail 整数)或标准错误。理想情况下,我想要一种获取所有三个的方法(return 代码、stdout、stderr)。
我会满足于 stdout 和 stderr。我在想我需要添加另一个管道,但我真的看不出第一个管道是如何设置来获取 stdout 的,所以我想不出我将如何更改它来同时获取这两个管道。
有人知道如何做到这一点,或者有其他可行的方法吗?
更新
查看我的完整示例 here 以及输出:
Start
1 res: /home
2 res: stdout
stderr
3 res:
End
您可以看到 3 res:
不会像 2 res: stdout
那样打印 stderr,但是 stderr 只是由进程(而不是我的程序)在单独的一行上转储到屏幕上).
外部库
我真的不想使用像 Qt 和 boost 这样的外部库——主要是因为我想要它的可移植性,而且我从事的许多项目都不使用 boost。但是,我将标记包含这些选项的解决方案,因为它们对其他用户有效:)
使用Comments/Answer
的完整解决方案
感谢大家的回答/评论,这里是修改后的解决方案(可运行):
来自 popen
的手册页:
The pclose() function waits for the associated process to terminate and returns the exit status of the command as returned by wait4(2).
因此,您自己调用 pclose()
(而不是使用 std::shared_ptr<>
的析构函数)将为您提供进程的 return 代码(如果进程没有,则阻塞)已终止)。
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
auto pipe = popen(cmd, "r"); // get rid of shared_ptr
if (!pipe) throw std::runtime_error("popen() failed!");
while (!feof(pipe)) {
if (fgets(buffer.data(), 128, pipe) != nullptr)
result += buffer.data();
}
auto rc = pclose(pipe);
if (rc == EXIT_SUCCESS) { // == 0
} else if (rc == EXIT_FAILURE) { // EXIT_FAILURE is not used by all programs, maybe needs some adaptation.
}
return result;
}
使用 popen()
获取 stderr 和 stdout,恐怕您需要通过添加 [=16 将传递给 popen() 的命令行的 stderr 输出重定向到 stdout =].这有两个流不可预测地混合的不便之处。
如果您真的想为 stderr 和 stdout 使用两个不同的文件描述符,一种方法是自己进行分叉并将新进程 stdout/stderr 复制到两个管道,这两个管道可从父进程。 (参见 dup2()
和 pipe()
)。我可以在这里详细介绍,但这是一种非常乏味的做事方式,必须格外小心。互联网上到处都是例子。
有一种可能的解决方法。您可以通过将“2>&1”附加到您的 cmd 来将 stderr 重定向到 stdout。这符合您的需求吗?
您可以使用自定义删除器从管道中获取 return 代码:
#include <cstdio>
#include <iostream>
#include <memory>
#include <string>
#include <array>
#include <utility>
using namespace std;
pair<string, int> exec(const char* cmd) {
array<char, 128> buffer;
string result;
int return_code = -1;
auto pclose_wrapper = [&return_code](FILE* cmd){ return_code = pclose(cmd); };
{ // scope is important, have to make sure the ptr goes out of scope first
const unique_ptr<FILE, decltype(pclose_wrapper)> pipe(popen(cmd, "r"), pclose_wrapper);
if (pipe) {
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
}
}
return make_pair(result, return_code);
}
int main(int argc, char* argv[]) {
if (argc <= 1) return 0;
cout << "calling with " << argv[1] << '\n';
const auto process_ret = exec(argv[1]);
cout << "captured stdout : " << '\n' << process_ret.first << endl;
cout << "program exited with status code " << process_ret.second << endl;
return 0;
}
下面是 答案的一个稍微不同的版本,它使用 fread()
而不是 fgets()
。
Antti Haapala 在此主题中建议使用 fread()
:
How do I execute a command and get the output of the command within C++ using POSIX?
此版本将标准输出读取到 std::string
,必须作为第二个参数传递。
函数 returns 执行 cmd
的退出代码,从 pclose()
返回。
要同时获得 stderr
,请添加到 cmd
的末尾:"2>&1"
,正如 在同一主题中所建议的那样。
下面的main()
函数,展示了如何使用:
int execute(std::string cmd, std::string& output)
列出当前目录并将其打印到标准输出:
#include <iostream>
#include <array>
#include <unistd.h>
int execute(std::string cmd, std::string& output) {
const int bufsize=128;
std::array<char, bufsize> buffer;
auto pipe = popen(cmd.c_str(), "r");
if (!pipe) throw std::runtime_error("popen() failed!");
size_t count;
do {
if ((count = fread(buffer.data(), 1, bufsize, pipe)) > 0) {
output.insert(output.end(), std::begin(buffer), std::next(std::begin(buffer), count));
}
} while(count > 0);
return pclose(pipe);
}
int main(int argc, char** argv) {
std::string output;
execute("ls", output);
std::cout << output;
}
给出以下答案(第一个 c++11 答案):
How do I execute a command and get the output of the command within C++ using POSIX?
为方便起见,下面是实现:
#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;
}
这非常适合执行命令(例如 std::string res = exec("ls");
)并将标准输出转换为字符串。
但它不做的是获取命令 return 代码(pass/fail 整数)或标准错误。理想情况下,我想要一种获取所有三个的方法(return 代码、stdout、stderr)。
我会满足于 stdout 和 stderr。我在想我需要添加另一个管道,但我真的看不出第一个管道是如何设置来获取 stdout 的,所以我想不出我将如何更改它来同时获取这两个管道。
有人知道如何做到这一点,或者有其他可行的方法吗?
更新
查看我的完整示例 here 以及输出:
Start
1 res: /home
2 res: stdout
stderr
3 res:
End
您可以看到 3 res:
不会像 2 res: stdout
那样打印 stderr,但是 stderr 只是由进程(而不是我的程序)在单独的一行上转储到屏幕上).
外部库
我真的不想使用像 Qt 和 boost 这样的外部库——主要是因为我想要它的可移植性,而且我从事的许多项目都不使用 boost。但是,我将标记包含这些选项的解决方案,因为它们对其他用户有效:)
使用Comments/Answer
的完整解决方案感谢大家的回答/评论,这里是修改后的解决方案(可运行):
来自 popen
的手册页:
The pclose() function waits for the associated process to terminate and returns the exit status of the command as returned by wait4(2).
因此,您自己调用 pclose()
(而不是使用 std::shared_ptr<>
的析构函数)将为您提供进程的 return 代码(如果进程没有,则阻塞)已终止)。
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
auto pipe = popen(cmd, "r"); // get rid of shared_ptr
if (!pipe) throw std::runtime_error("popen() failed!");
while (!feof(pipe)) {
if (fgets(buffer.data(), 128, pipe) != nullptr)
result += buffer.data();
}
auto rc = pclose(pipe);
if (rc == EXIT_SUCCESS) { // == 0
} else if (rc == EXIT_FAILURE) { // EXIT_FAILURE is not used by all programs, maybe needs some adaptation.
}
return result;
}
使用 popen()
获取 stderr 和 stdout,恐怕您需要通过添加 [=16 将传递给 popen() 的命令行的 stderr 输出重定向到 stdout =].这有两个流不可预测地混合的不便之处。
如果您真的想为 stderr 和 stdout 使用两个不同的文件描述符,一种方法是自己进行分叉并将新进程 stdout/stderr 复制到两个管道,这两个管道可从父进程。 (参见 dup2()
和 pipe()
)。我可以在这里详细介绍,但这是一种非常乏味的做事方式,必须格外小心。互联网上到处都是例子。
有一种可能的解决方法。您可以通过将“2>&1”附加到您的 cmd 来将 stderr 重定向到 stdout。这符合您的需求吗?
您可以使用自定义删除器从管道中获取 return 代码:
#include <cstdio>
#include <iostream>
#include <memory>
#include <string>
#include <array>
#include <utility>
using namespace std;
pair<string, int> exec(const char* cmd) {
array<char, 128> buffer;
string result;
int return_code = -1;
auto pclose_wrapper = [&return_code](FILE* cmd){ return_code = pclose(cmd); };
{ // scope is important, have to make sure the ptr goes out of scope first
const unique_ptr<FILE, decltype(pclose_wrapper)> pipe(popen(cmd, "r"), pclose_wrapper);
if (pipe) {
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
}
}
return make_pair(result, return_code);
}
int main(int argc, char* argv[]) {
if (argc <= 1) return 0;
cout << "calling with " << argv[1] << '\n';
const auto process_ret = exec(argv[1]);
cout << "captured stdout : " << '\n' << process_ret.first << endl;
cout << "program exited with status code " << process_ret.second << endl;
return 0;
}
下面是 fread()
而不是 fgets()
。
Antti Haapala 在此主题中建议使用 fread()
:
How do I execute a command and get the output of the command within C++ using POSIX?
此版本将标准输出读取到 std::string
,必须作为第二个参数传递。
函数 returns 执行 cmd
的退出代码,从 pclose()
返回。
要同时获得 stderr
,请添加到 cmd
的末尾:"2>&1"
,正如
main()
函数,展示了如何使用:
int execute(std::string cmd, std::string& output)
列出当前目录并将其打印到标准输出:
#include <iostream>
#include <array>
#include <unistd.h>
int execute(std::string cmd, std::string& output) {
const int bufsize=128;
std::array<char, bufsize> buffer;
auto pipe = popen(cmd.c_str(), "r");
if (!pipe) throw std::runtime_error("popen() failed!");
size_t count;
do {
if ((count = fread(buffer.data(), 1, bufsize, pipe)) > 0) {
output.insert(output.end(), std::begin(buffer), std::next(std::begin(buffer), count));
}
} while(count > 0);
return pclose(pipe);
}
int main(int argc, char** argv) {
std::string output;
execute("ls", output);
std::cout << output;
}