从 c++ 中的实例化进程获取输出的可靠方法是什么?

What is a reliable way to get output from instantiated processes in c++?

本文关键字:方法 输出 是什么 获取 c++ 实例化 进程      更新时间:2023-10-16

基本上我想做的是在Linux shell中实例化一个程序,并将它发出的输出获取到标准输出到字符串或字符串列表中(每行,取决于(。我想在我的c ++应用程序中运行的程序只是一个ps aux

  • 找到的所有"解决方案"都没有完全解决我的问题。他们中的大多数都属于本答案中描述的错误。基本上popen失败,并且不会返回 shell 执行的完整输出。为了这个问题,我们称之为"错误 1"。

到目前为止我尝试过:

1(使用boost,我按照他们的文档尝试了这个:

#include <boost/process.hpp>
namespace bp = boost::process;
bool is_process_running(std::string p_name){
string cmd = "ps aux";
bp::ipstream out;
std::string line;
bp::child c(cmd, bp::std_out > out);
// the while is supposed to read each line of the output
// but the execution doesn't even enter the while
while(c.running() && std::getline(out, line) && !line.empty())
{
if(line.find(p_name) != std::string::npos)
{
return true;
}
}
c.wait();
return false;
}

2(我也尝试过:

bool is_process_running(std::string p_name){
string cmd = "ps aux";
std::array<char, 256> buffer;
std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) throw std::runtime_error("popen() failed!");
while (!feof(pipe.get())) {
if (fgets(buffer.data(), 256, pipe.get()) != nullptr)
{
// here the line comes incomplete
std::string line(buffer.data());
if(line.find(p_name) != std::string::npos)
{
return true;
}
}
}
return false;
}

但最后一个也陷入了"错误1"。

3(这个代码片段落入"错误1">
4(这个也落入"错误1"
我不这样做,有必要输入 3( 和 4( 的代码,因为它实际上是这些答案中的内容,我没有更改任何东西,但如果你们需要,我可以编辑我的问题。所以我的问题就是这个,如何以一种可行的方式获取命令输出?提前谢谢。

编辑: 我已经尝试使用提供的代码截取

bool is_process_running(std::string p_name)
FILE* p = popen(cmd.c_str(), "r");
if (!p) { cerr << "oops"; return 2; }
char line[500];
while (fgets(line, 500, p))
{
std::string linha(line);
if(linha.find(p_name) != std::string::npos)
return true;
}
fclose(p);
return false;
}

在这种情况下,下面是 popen/fgets 截断输出的示例:

[fabio ~]$ ps aux | grep 391
root       391  0.0  0.1  48580 12864 ?        Ss   Sep03   0:06 /usr/lib/systemd/systemd-journald
fabio 15435  0.0  0.0 112548   960 pts/2    S+   15:40   0:00 grep --color=auto 391

进程 391 的行与此行相同,但在运行时它只返回我"root 391 0.0 0.1 48580 12856 ? Ss Sep03 0:06 /usr/lib/systemn"

直接使用相关的系统调用:pipe(2(、fork(2(、dup2(2(、close(2(、execve(2(、waitpid(2(。阅读一些好的Linux编程书籍(也许是旧的ALP,其中有几个章节与您的问题相关(。在处理管道的其他自由软件程序的源代码中寻找灵感(例如,一些简单的shell,如sash(和/或libc(或musl-libc,它具有非常可读的代码(以获取popen代码。

我们没有空间和时间来详细解释,但你会在网上找到很多资源,ALP是可以免费下载的;当然execve是通常在子进程中运行的最后一个系统调用,因为它正在重新初始化虚拟地址空间。

在不限制线长度的情况下从管道读取。要么手动使用 read(2( (并关心缓冲(,要么考虑 getline(3( (另请参阅 fdopen(3((

您可能对POCO或Qt等框架感兴趣。

你可能想避免使用 popen(3( (并且你不需要使用/bin/sh的东西,除非你的命令需要扩展以进行通配; 参见 glob(7((。

注意信号(7(和信号安全(7((也许还有民意调查(2(...


我想在 c++ 应用程序中运行的程序只是一个ps aux

那么你可能不应该运行任何外部进程,而是直接访问/proc/(参见 proc(5(;你需要 opendir(3(、readdir(3( 等(,或者通过一些libproc。当然,ps aux是访问/proc/本身(因此,除了运行ps的一些简单性之外,您不会获得任何东西,并且使用它会失去一些性能和一些健壮性(。另请参阅这个,这个,这个,这个,这个答案。

在您提到的评论中

我需要知道某些进程是否在给定其名称和参数的情况下正在运行。

这是以编程方式访问/proc/的一个很好的例子:使用opendir循环/proc/目录,循环readdirenddir。对于readdir给出的每个条目,请检查它是否具有数字名称(以数字开头(,否则跳过它。使用该数字名称(例如1234(构造一个类似/proc/1234/cmdline的路径并打开它,然后解析其内容(它具有分隔的NUL字节(。这可能并不比启动ps aux过程、获取和解析其输出更难,而且肯定更有效。(要了解详细信息,请运行od -cx /proc/self/cmdline然后od -cx /proc/$(pidof systemd-journald)/cmdline并了解其输出(。