linux c++ popen 使用 多个命令,c++ - popen()如何工作以及如何在Linux上将其实现为C ++代码? - 堆栈内存溢出...

如何使用popen()将stdout从Linux的子程序中捕获到主C ++程序

您熟悉UNIX哲学吗? 您可能会阅读Wikipedia文章“ UNIX Philosophy”的开头。 这是一个片段:

UNIX哲学由Doug McIlroy [1]在1978年的《贝尔系统技术杂志》上发表:[2]

1)让每个程序做好一件事情。 要完成一项新工作,请重新构建而不是通过添加新的“功能”使旧程序复杂化。

2)期望每个程序的输出都将成为另一个程序(尚不为人所知)的输入。 不要将多余的信息弄乱。 避免严格使用列或二进制输入格式。 不要坚持交互式输入。

... 和更多 ...

我更喜欢popen访问许多我认为有用的Linux工具。 示例包括sort,unique和sha256sum,以及其他一些示例。 但是您可能会注意到,许多等效项正在进入C ++库(std :: sort等),并且在努力下,我发现例如sha256sum的代码适合在我的c ++应用程序中进行编译。

1)启动后,popen被赋予一个字符串,该字符串可以一部分标识要运行的应用程序,也可以分为多个部分,第一部分是一个应用程序,附加字符串是传递给该应用程序的参数,就像命令行一样输入。 就像在外壳(提示)中一样

2)在“ r”模式下启动时,sha256sum的stdout管道被定向到由popen调用打开的FILE *句柄中。 因此,输出将“反馈”到您的程序。

但是我不明白这些东西是如何工作的。

这不是真正有用的评论,也不是问题。 您的代码片段对我来说直截了当(尽管基本上是C代码)。 也许您应该选择一行代码并提出一些具体的要求?

但是,在您对代码进行过多工作之前,让我进行一些初步调查,在那我们将手动运行希望与popen一起使用的代码,并捕获结果以供您查看应用程序的输出。

调查1-

在您的代码段中,您正在尝试命令“哪一个漂流网”。 当您的PATH上不存在程序driftnet时,这是一个不幸的选择。

我系统上的结果:

$ which driftnet

$

由于该命令不存在,因此命令“哪个”仅返回空结果。

很难判断我们是否做错了什么。 没有流网,我也无济于事。

调查2-更为有趣的审判。

sha256sum需要输入文件...在这里,我提供了“ C ++ Hello World”程序,您可以将其捕获到文件“ HelloWorld.cc”(或任何您想要的位置),或使用任何自己的文件。

// If your C++ 'Hello World' has no class ... why bother?

#include

class Hello_t {

public:

Hello_t() { std::cout << "\n Hello" << std::flush; }

~Hello_t() { std::cout << "World!" << std::endl; }

void operator() () { std::cout << " C++ "; }

};

int main(int, char**) { Hello_t()(); }

总共10行代码(以注释开头)在单个include之后有1个空行。 我将编辑器设置为自动神奇地删除尾随空格。 如果您留下空白,您的结果可能会有所不同。

因此,从命令行计算文件“ HelloWorld.cc”的sha256sum时会发生什么? 无需猜测...只需尝试一下!

在命令行上,调用命令并在下一行查看其响应:

$ sha256sum HelloWorld.cc

f91c6c288325fb4f72546482a9b4f1fa560f05071a12aa2a1268474ea3eeea3e HelloWorld.cc

或许你可以尝试用你和sha256sum已知的文件POPEN代码?

现在,您可以看到输出(您的popen代码)可能包含的内容。 对于此调查,**命令sha256sum结果是1个长字符串,其中带有空格。

仅供参考-几个月前,在对我的库进行检修时,我破坏了我的C ++ popen代码。 修复后,我计划重新考虑您的工作,以了解您的进度。

如果您的代码开始工作,我建议您提交自己的答案!

如果您可以确定更具体的问题,请对该问题进行细化。

我建议您使用C ++代码...如果您要学习一些东西,并且想学习C ++,则不必研究所发现的内容。

我的代码再次正常工作。 (更新2017/8/2)

但是,首先让我以不同的顺序分享UNIX哲学的一些后果:

后果1-管道和过滤器

筛选器是可以做一件事的程序。 他们接受输入流,并产生输出流,通常“删除”某些东西。

在代码指标领域,管理人员可以查看行数以了解进度(行数增长变慢)和问题(而不是变慢)。

每个人对代码指标都有自己的见解...并非文件中的每一行都应计算在内。 例如,我认为空格很重要,但是不应将一行空格视为代码。 同样,完整的注释行也不是代码。 恕我直言。

在以下命令中

f1 < pfn | f2 | f3 | f4

f1..fn是过滤器,所有过滤器均作为一个命令提交给外壳。 在我的代码指标工具中,我创建了

f1: ebl erase blank lines

f2: ecppc erase cpp comments

f3: ecc erase c comments

and several more.

从而

ebl < HelloWorld.cc | ecppc | ecc | ebl | wc

是提交给外壳的单个字符串,最终提示cout的3个数字。

UNIX之类的命令“ wc”可能存在于您的系统上。 在Ubuntu 15.10上:

$ wc < HelloWorld.cc

10 52 309

指示HelloWorld.cc有10行(不过滤)。

后果2-命令列表

命令列表是一个外壳命令的列表,每个命令都能完成一件事。 该列表标识了执行的时间顺序。 比管道和过滤器更一般的想法。

例如,我用分配的键(F8)设置了编辑器(emacs),以提供以下编译命令:

USER_FLAGS='-O0 ' ; export USER_FLAGS ; time make CC='g++-5 -m64 ' dumy514 ; ./dumy514

特定文件pfn可能只输入一次,但是保存在emacs内的历史记录缓冲区中,因此无需重新输入它们。

请注意3个分号。 它们在提交给Linux命令外壳程序的一个字符串中分隔多个命令。 3个元素的命令列表:设置环境变量以覆盖默认选项; 导出新标志以使其可用于编译器; 并开始制作。

所以? popen()接受命令列表。 像shell这样的“命令列表”可能会接受。

我终于仔细阅读了“ man popen”的回复。

根据说明:

popen()函数通过创建管道,分叉并调用shell来打开进程。

您给popen的命令在shell中运行。

但是我不明白这些东西是如何工作的。

现在,这个“东西”应该开始变得有意义了。 我们正在访问外壳以处理命令/命令列表/管道过滤器。 这将启动“其他”过程,并使用PIPE与第一个过程进行交互。

那么在shell中处理的命令输出如何传递给我的代码呢? 接下来是一些理解的线索。 其中包括一些单元测试演示,您可以在其中轻松更改popen cmd,然后编译并运行新的cmd。

以下提供了在两种模式之一中使用popen的代码。 是的,它们属于3类,一个基础和两个派生类。 希望这可以让您一次运行多个。 (在我的2核计算机上不是很有用。)随时重构代码,仅保留您所需的内容。 其余的将在这里供您继续指导。

#include

#include

#include

#include // sterror

#include // popen, FILE, fgets, fputs

#include

class POpen_t // access to ::popen

{

protected:

FILE* m_FILE;

std::string m_cmd;

public:

POpen_t(void) : m_FILE(nullptr)

{ }

virtual ~POpen_t(void) {

if (m_FILE) (void)close();

m_FILE = 0;

}

// on success: 0 == return.size(), else returns error msg

std::string close()

{

std::stringstream errSS;

do // poor man's try block

{

// pclose() returns the term status of the shell cmd

// otherwise -1 and sets errno.

assert(nullptr != m_FILE); // tbr - some sort of logic error

// tbr if(0 == m_FILE) break; // success?

int32_t pcloseStat = ::pclose(m_FILE);

int myErrno = errno;

if (0 != pcloseStat)

{

errSS << "\n POpen_t::close() errno " << myErrno

<< " " << std::strerror(myErrno) << std::endl;

break;

}

m_FILE = 0;

}while(0);

return(errSS.str());

} // std::string close(void)

}; // class POpen_t

我认为复制/粘贴将这些内容分成3个部分会更容易。 但是请记住,我将所有代码都放在一个文件中。 不是单个头文件,但对我有用。 如果愿意,可以将其重构为.h和.cc文件。

class POpenRead_t : public POpen_t // access to ::popen read-mode

{

public:

POpenRead_t(void) { }

// ::popen(aCmd): opens a process (fork), invokes shell,

// and creates a pipe

// returns NULL if the fork or pipe calls fail,

// or if it cannot allocate memory.

// on success: 0 == return.size(), else returns error msg

std::string open (std::string aCmd)

{

std::stringstream errSS; // 0 == errSS.str().size() is success

assert (aCmd.size() > 0);

assert (0 == m_FILE); // can only use serially

m_cmd = aCmd; // capture

do // poor man's try block

{

if(true) // diagnosis only

std::cout << "\n POpenRead_t::open(cmd): cmd: '"

<< m_cmd << "'\n" << std::endl;

// ::popen(aCmd): opens a process by creating a pipe, forking,

// and invoking the shell.

// returns NULL if the fork or pipe calls fail,

// or if it cannot allocate memory.

m_FILE = ::popen (m_cmd.c_str(), "r"); // create 'c-stream' (FILE*)

// ^^ function is not in namespace std::

int myErrno = errno;

if(0 == m_FILE)

{

errSS << "\n POpenRead_t::open(" << m_cmd

<< ") popen() errno " << myErrno

<< " " << std::strerror(myErrno) << std::endl;

break;

}

} while(0);

return (errSS.str());

} // std::string POpenRead_t::open(std::string aCmd)

// success when 0 == errStr.size()

// all outputs (of each command) captured into captureSS

std::string spinCaptureAll(std::stringstream& captureSS)

{

const int BUFF_SIZE = 2*1024;

std::stringstream errSS; // capture or error

do

{

if(0 == m_FILE)

{

errSS << "\n ERR: POpenRead_t::spinCaptureAll(captureSS) - m_FILE closed";

break;

}

size_t discardedBlankLineCount = 0;

do

{

// allocate working buff in auto var, fill with nulls

char buff[BUFF_SIZE] = { 0 };

if(true) { for (int i=0; i

// char * fgets ( char * str, int num, FILE * c-stream );

// Reads characters from c-stream and stores them as a C string

// into buff until

// a) (num-1) characters have been read

// b) a newline or

// c) the end-of-file is reached

// whichever happens first.

// A newline character makes fgets stop reading, but it is considered

// a valid character by the function and included in the string copied

// to str

// A terminating null character is automatically appended after the

// characters copied to str.

// Notice that fgets is quite different from gets: not only fgets

// accepts a c-stream argument, but also allows to specify the maximum

// size of str and includes in the string any ending newline character.

// fgets() returns buff or null when feof()

char* stat = std::fgets(buff, // char*

BUFF_SIZE, // count - 1024

m_FILE); // c-stream

assert((stat == buff) || (stat == 0));

int myErrno = errno; // capture

if( feof(m_FILE) ) { // c-stream eof detected

break;

}

// when stat is null (and ! feof(m_FILE) ),

// even a blank line contains "any ending newline char"

// TBD:

// if (0 == stat) {

// errSS << "0 == fgets(buff, BUFF_SIZE_1024, m_FILE) " << myErrno

// << " " << std::strerror(myErrno) << std::endl;

// break;

// }

if(ferror(m_FILE)) { // file problem

errSS << "Err: fgets() with ferror: " << std::strerror(myErrno);

break;

}

if(strlen(buff)) captureSS << buff; // additional output

else discardedBlankLineCount += 1;

}while(1);

if(discardedBlankLineCount)

captureSS << "\n" << "discarded blank lines: " << discardedBlankLineCount << std::endl;

} while(0);

return (errSS.str());

} // std::string POpenRead_t::spinCaptureAll(std::stringstream& ss)

}; // class POpenRead_t

现在POpenWrite_t:

class POpenWrite_t : public POpen_t // access to ::popen

{

public:

POpenWrite_t(void) { }

// ::popen(aCmd): opens a process (fork), invokes the non-interactive shell,

// and creates a pipe

// returns NULL if the fork or pipe calls fail,

// or if it cannot allocate memory.

// on success: 0 == return.size(), else returns error msg

std::string open (std::string aCmd)

{

std::stringstream errSS; // 0 == errSS.str().size() is success

assert (aCmd.size() > 0);

assert (0 == m_FILE);

m_cmd = aCmd; // capture

do // poor man's try block

{

if(true) // diagnosis only

std::cout << "\n POpenWrite_t::open(cmd): cmd: \n '"

<< "'" << m_cmd << std::endl;

m_FILE = ::popen (m_cmd.c_str(), "w"); // use m_FILE to write to sub-task std::in

int myErrno = errno;

if(0 == m_FILE)

{

errSS << "\n POpenWrite_t::open(" << m_cmd

<< ") popen() errno " << myErrno

<< " " << std::strerror(myErrno) << std::endl;

break;

}

} while(0);

return (errSS.str());

} // std::string POpenWrite_t::open(std::string aCmd)

// TBR - POpenWrite_t::write(const std::string& s)

// work in progress - see demo write mode

}; // class POpenWrite_t

接下来,我称之为T514_t,该类定义了我的单元测试或演示工作。 条目位于“ exec()”中。

事实证明,popen中使用的外壳可能与您在终端中遇到的外壳不同。 在会话启动期间,shell的一部分决定它是处于交互(终端)模式还是非交互(无终端)模式。 在我的系统上,终端/交互式shell是'bash',而popen /非交互式shell是'sh'。 为popen开发shell命令时,请记住这一点。

POpenRead_t的有趣用法是在方法“ std :: string shellCheck(std :: stringstream&rsltSS)”中。

class T514_t

{

const std::string line = "\n------------------------------------------------------";

const char escape = 27;

public:

T514_t() = default;

~T514_t() = default;

std::string exec (int argc, char* argv[],

std::stringstream& resultSS )

{

std::string errStr;

errStr = shellCheck(resultSS); // when not bash, make a choice

if (0 == errStr.size()) // shell is expected

{

// bash reported, continue

switch (argc)

{

case 2 : { errStr = exec (argv, resultSS); } break;

default: { errStr = " User error"; usage(argv); } break;

} // switch (argc)

}

return (errStr); // when no err, errStr.size() is 0

} // int exec()

private:

std::string exec(char* argv[], std::stringstream& resultSS)

{

std::string errStr;

std::cout << clrscr() << std::flush;

switch (std::atoi(argv[1]))

{

case 1:

{

std::cout << clrscr() << boldOff() << line;

errStr = demoReadMode(resultSS);

std::cout << boldOff() << std::endl;

} break;

case 2:

{

std::cout << clrscr() << boldOn() << line;

errStr = demoWriteMode();

std::cout << boldOff() << std::endl;

} break;

default: { usage(argv); } break;

} // switch (std::atoi(argv[1]))

return(errStr);

} // int exec(char* argv[], std::stringstream& resultSS)

// Ubuntu has 5 (or more) shells available

// 1) Bourne shell (' sh'), 2) C shell (' csh'), 3) TC shell (' tcsh'),

// 4) Korn shell (' ksh'), 5) Bourne Again shell (' bash')

//

// bash is my interactive shell

// sh is my non-interactive shell

// which (mildly) influences the cmds of the demo's

//

// when not bash, what do you want to do?

std::string shellCheck(std::stringstream& rsltSS)

{

std::stringstream errValSS;

std::string cmd;

cmd += "ps -p \"$$\" ";

{

POpenRead_t popenR;

errno = 0;

std::string rStat = popenR.open(cmd);

int myErrno = errno;

if(0 != rStat.size()) {

errValSS << "\n Err: " << cmd << " failed. rStat: "

<< std::strerror(myErrno) << std::endl;

return(errValSS.str());

}

do

{

errno = 0;

std::string errSS = popenR.spinCaptureAll (rsltSS);

myErrno = errno;

if (false) { // dianosis only

std::cout << "\n demoReadMode() ss/myErrno/s:\n"

<< rsltSS.str() << " "

<< myErrno << " '"

<< errSS << "'" << std::endl;

}

break;

if(0 != myErrno)

{

errValSS << "\n Err: popenR.spinCaputureAll(): cmd / strerror(myErrno): "

<< cmd << " / " << std::strerror(myErrno) << std::endl;

return(errValSS.str());

}

if (0 == rsltSS.str().size()) { // TBR

std::cout << "\n demoReadMode: ss.str().size() is 0, indicating completed" << std::endl;

} break;

}while(1);

// NOTE: pclose() returns the termination status of the shell command

// otherwise -1 and sets errno.

// errno = 0;

(void)popenR.close(); // return value is term status of the shell command

if(errno != 0)

{

errValSS << "\n Err: POpen_t::close() - cmd: " << cmd

<< " err:" << std::strerror(errno) << std::endl;

}

}

std::string s = rsltSS.str();

std::string nishell (" sh"); // non-interactive shell expected is: " sh"

size_t indx = s.find(nishell); // my interactive shell is bash

// clear

rsltSS.str(std::string()); rsltSS.clear(); // too much info

if (std::string::npos != indx)

{

// normally I would not include a 'success' message (not the 'UNIX pholosopy'),

// but here I have added to the (success) results:

if(true) //

rsltSS << "\n the reported non-interactive shell is the required '"

<< nishell << "' ... continuing.\n" << std::endl;

}

else

{

// TBR - when non-interactive shell is unexpectedly different that nishell,

// this demo code aborts ... (with no results) and the error msg:

errValSS << "\n the reported non-interactive shell is not " << nishell

<< " \n" << s << "\n ... aborting\n";

// alternative actions are _always_ possible

// untested examples:

// the shell can be temporarily changed as part of cmd (untested)

// the user could change the non-interactive shell

// your use of popen (POpen_t classes above)

// can change 'cmd's based on the discovered shell

}

return(errValSS.str());

} // std::string shellCheck(std::stringstream& rsltSS)

std::string demoReadMode(std::stringstream& rsltSS)

{

std::stringstream errValSS;

std::string cmd;

int i = 1;

cmd += "./HelloWorld ; ";

cmd += "echo " + std::to_string(i++) + " ; ";

cmd += "./HelloWorld ; ";

cmd += "sha256sum HelloWorld.cc ; ";

cmd += "echo " + std::to_string(i++) + " ; ";

cmd += "./HelloWorld ; ";

cmd += "echo TEST WORKS ; ";

cmd += "./HelloWorld";

{

POpenRead_t popenR;

errno = 0;

std::string rStat = popenR.open(cmd);

int myErrno = errno;

if(0 != rStat.size()) {

errValSS << "\n Err: " << cmd << " failed. rStat: "

<< std::strerror(myErrno) << std::endl;

return(errValSS.str());

}

do

{

errno = 0;

std::string errSS = popenR.spinCaptureAll (rsltSS);

myErrno = errno;

if (false) { // dianosis only

std::cout << "\n demoReadMode() ss/myErrno/s:\n"

<< rsltSS.str() << " "

<< myErrno << " '"

<< errSS << "'" << std::endl;

}

break;

if(0 != myErrno)

{

errValSS << "\n Err: popenR.spinCaputureAll(): cmd / strerror(myErrno): "

<< cmd << " / " << std::strerror(myErrno) << std::endl;

return(errValSS.str());

}

if (0 == rsltSS.str().size()) { // TBR

std::cout << "\n demoReadMode: ss.str().size() is 0, indicating completed" << std::endl;

} break;

}while(1);

// NOTE: pclose() returns the termination status of the shell command

// otherwise -1 and sets errno.

// errno = 0;

(void)popenR.close(); // return value is term status of the shell command

if(errno != 0)

{

errValSS << "\n Err: POpen_t::close() - cmd: " << cmd

<< " err:" << std::strerror(errno) << std::endl;

}

}

return(errValSS.str());

} // std::string demoReadMode(std::stringstream& rsltSS)

std::string demoWriteMode()

{

std::stringstream errValSS;

std::string cmd;

int i = 1;

cmd += "./HelloWorld ; ";

cmd += "echo " + std::to_string(i++) + " ; ";

cmd += "./HelloWorld ; ";

cmd += "sha256sum HelloWorld.cc ; ";

cmd += "echo " + std::to_string(i++) + " ; ";

cmd += "./HelloWorld ; ";

{

POpenWrite_t popenW; // popen in write mode

// errno = 0;

std::string wStat = popenW.open(cmd);

int myErrno = errno;

if (0 != wStat.size()) {

errValSS << "\n Err: " << cmd << "\n failed. wStat: "

<< std::strerror(myErrno) << std::endl;

return(errValSS.str());

}

// tbd - Work in Progress - what command to receive what data from here

// login - needs root, tbr - can cmd contain sudo? probably

//

//

// NOTE: pclose() returns the termination status of the shell command

// otherwise -1 and sets errno.

// errno = 0;

(void)popenW.close(); // return value is term status of the shell command

if(errno != 0)

{

errValSS << "\n Err: POpen_t::close() - cmd: " << cmd

<< " err:" << std::strerror(errno) << std::endl;

}

}

return(errValSS.str());

} // std::string demoWriteMode()

void usage(char* argv[])

{

std::cout << " executable: "<< argv[0]

<< "\n USAGE - user must select test mode"

<< "\n test"

<< "\n 1 - demoReadMode"

<< "\n 2 - demoWriteMode"

<< std::endl;

}

std::string boldOff(){std::string rV; rV.push_back(escape); rV += "[21m"; return(rV); } // bold off

std::string boldOn() {std::string rV; rV.push_back(escape); rV += "[1m"; return(rV); } // bold on

inline std::string clrscr(void) {

std::stringstream ss;

ss << static_cast(escape) << "[H" // home

<< static_cast(escape) << "[2J"; // clrbos

return(ss.str());

}

}; // class T514_t

最后,我的主要工作……我只想在这里做几件事。

int main(int argc, char* argv[])

{

std::string errStr;

std::stringstream resultSS;

{

T514_t t514;

errStr = t514.exec(argc, argv, resultSS);

}

// display result

if (resultSS.str().size())

std::cout << "\n\n "<< resultSS.str() << std::endl;

if(errStr.size())

std::cerr << errStr << std::endl;

return(static_cast(errStr.size()));

}

因此,构建并使用std :: string cmd; 以您感兴趣的模式。祝您好运。

欢迎提问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>