Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Run program and take first line from its stdout

127 views
Skip to first unread message

Frederick Gotham

unread,
Mar 9, 2020, 7:18:20 AM3/9/20
to

I'm using "boost::process" to start a second program.

I want to write a robust function to start a second program and to retrieve the first line of text from its stdout. If any kind of error takes places, the function will simply return an empty string. No exceptions will be thrown.

When I say the first line of text, I mean either of these two things:
(1) Retrieve bytes from stdout until a new line is encountered, and discard the new line character.
(2) Retrieve bytes from stdout until EOF.

I will give a default timeout of 5 seconds for the second program to finish.

So I start off with this:

#include <boost/process.hpp> /* boost::process::child, ipstream */
#include <boost/chrono.hpp> /* boost::chrono::seconds */
#include <boost/thread.hpp> /* try_join_for */

#include <string> /* string */

std::string Run_Command_string(std::string const &str_prog, unsigned const timeout = 5u) noexcept
{
using std::string;
namespace bp = boost::process;

try
{
string retval;

bp::ipstream ip;

/* Start second thread here to capture one line of stdout */
boost::thread reader(
[&retval, &ip]
{
try
{
/* Retain the first line of text */
std::getline(ip,retval);

/* Now just discard the rest of the output */
for (string tmp; std::getline(ip,tmp); ) { /* Do Nothing */ }
}
catch (...)
{
try { retval.clear(); } catch (...) { /* Do Nothing */ }
}
}
);

bp::child c(str_prog, bp::std_in.close(), bp::std_out > ip, bp::std_err > bp::null);

if ( reader.try_join_for(boost::chrono::seconds(timeout)) )
{
/* Program has ended, so hopefully we have text */
return retval;
}
else
{
/* Program has frozen, so just return an empty string */
reader.interrupt();
// I think technically we're supposed to call 'join' here ???
return string();
}
}
catch (...)
{
return string();
}
}

#include <iostream>

using std::cout;
using std::endl;

auto main(void) -> int
{
//cout << Run_Command_string("ps aux | grep sbin") << endl;

/* Under Linux I have to do "sh -c" otherwise the piping from ps to grep doesn't work.
I tried using boost::process:shell but I couldn't get it to work. */

cout << Run_Command_string("sh -c \"ps aux | grep sbin\"") << endl;
}

Is there anything you'd change?

cda...@gmail.com

unread,
Mar 9, 2020, 8:30:06 AM3/9/20
to
Your brain.

cda...@gmail.com

unread,
Mar 9, 2020, 8:37:55 AM3/9/20
to
More to the point, the whole moron alert got triggered at the following line...

#include <string> /* string */

That's as bad as you inverting your conditionals in a previous post. I think maybe the only thing dumber would be a 12 yr old retarded girl having the following as a code comment...

return 1; /* returns 1 */

Frederick Gotham

unread,
Mar 9, 2020, 8:49:27 AM3/9/20
to
On Monday, March 9, 2020 at 12:37:55 PM UTC, cda...@gmail.com wrote:

> return 1; /* returns 1 */


You should explicitly indicate that it's signed:

return static_cast<signed int>(5); /* return 5 as a signed integer */

cda...@gmail.com

unread,
Mar 9, 2020, 8:59:58 AM3/9/20
to
You're a dumbass that needs to be banned from coming near a computer.

Frederick Gotham

unread,
Mar 9, 2020, 9:01:26 AM3/9/20
to
On Monday, March 9, 2020 at 12:59:58 PM UTC, cda...@gmail.com wrote:

> You're a dumbass that needs to be banned from coming near a computer.


Either meditate more, or give me your home address and I'll send you some beads.

Message has been deleted

cda...@gmail.com

unread,
Mar 9, 2020, 9:16:51 AM3/9/20
to
On Monday, March 9, 2020 at 6:14:28 AM UTC-7, cda...@gmail.com wrote:
> If you are trying to get this to run under Linux, and insist on using a PC, might I suggest that you give *BOTH* the computer and your right had a rest, and like read "Advanced Programming in the Unix Environment" by the late Dr. Stevens. After that, take a step back and ask yourself "Why I'm I using C++ to write a Linux program."
>
> This is words can't express the pure abomination on the following winner line of code..
>
> cout << Run_Command_string("sh -c \"ps aux | grep sbin\"") << endl;
>
>
> This is in addition to your retarded comments, coupled with your thinking it's okay to invert an expression because some jackass heathen fast food worker posted it on stackoverflow, and everything else.
>
> Well...here is some other advice.
>
> Step away from your computer. After that, disconnect your PC, open your windows, throw the thing in the trash, and then ask yourself "How come I still get find a person of the opposite sex who will date me?"


And apparently my I can't write this morning. Whatever. I need to get back to writing code at my job.

cda...@gmail.com

unread,
Mar 9, 2020, 9:19:15 AM3/9/20
to
If you are trying to get this to run under Linux, and insist on using a PC, might I suggest that you give *BOTH* the computer and your right had a rest, and like read "Advanced Programming in the Unix Environment" by the late Dr. Stevens. After that, take a step back and ask yourself "Why I'm I using C++ to write a Linux program."

Because the following in words can't express the pure abomination on the following winner line of code..

cout << Run_Command_string("sh -c \"ps aux | grep sbin\"") << endl;


This is in addition to your retarded comments, coupled with your thinking it's okay to invert an expression because some jackass heathen fast food worker posted it on stackoverflow, and everything else.

Well...here is some other advice.

Step away from your computer. After that, disconnect your PC, open your windows, throw the thing in the trash, and then ask yourself "How come I still find a person of the opposite sex who will date me?"



Frederick Gotham

unread,
Mar 9, 2020, 12:16:27 PM3/9/20
to
On Monday, March 9, 2020 at 11:18:20 AM UTC, Frederick Gotham wrote:

> I want to write a robust function to start a second program and to retrieve the first line of text from its stdout. If any kind of error takes places, the function will simply return an empty string. No exceptions will be thrown.


After testing my previous code, I've now got the following which works on Linux x64 and Linux armhf(32bit):

std::string Run_Command_string(std::string str_cmd, unsigned const timeout = 5u) noexcept
{
using std::string;
namespace bp = boost::process;

try
{
string retval;

bp::ipstream ip;

boost::replace_all(str_cmd, "\"", "\\\"");

cout << "sh -c \"" + str_cmd + "\"" << endl;

bp::child c(bp::search_path("sh"), "-c", str_cmd, bp::std_in.close(), bp::std_out > ip, bp::std_err > bp::null);

cout << "About to start other thread. . ." << endl;

/* Start second thread here to capture one line of stdout */
boost::thread reader(
[&retval, &ip]
{
try
{
/* Retain the first line of text */
cout << "About to try to get a line of text" << endl;
std::getline(ip,retval);
cout << "Got line of text: " << retval << endl;

/* Now just discard the rest of the output */
for (string tmp; std::getline(ip,tmp); ) { /* Do Nothing */ }
}
catch (...)
{
try { retval.clear(); } catch (...) { /* Do Nothing */ }
}
}
);

cout << "About to wait for other thread. . ." << endl;

if ( reader.try_join_for(boost::chrono::seconds(timeout)) )
{
/* Program has ended, so hopefully we have text */
return retval;
}
else
{
/* Program has frozen, so just return an empty string */
reader.interrupt();
// I think technically we're supposed to call 'join' here ???
return string();
}
}
catch (...)
{
cout << "Exception caught inside Run_Command_string" << endl;
return string();
}
}

I should be able to use "boost::process::shell" instead of doing "sh" "-c", but I just can't get the former to work.

Jorgen Grahn

unread,
Mar 10, 2020, 2:55:40 AM3/10/20
to
On Mon, 2020-03-09, Frederick Gotham wrote:
>
> I'm using "boost::process" to start a second program.
>
> I want to write a robust function to start a second program and to
> retrieve the first line of text from its stdout. If any kind of
> error takes places, the function will simply return an empty
> string. No exceptions will be thrown.
...
>
> I will give a default timeout of 5 seconds for the second program to finish.

Why? It's an unusual feature and complicates things.

> So I start off with this:
>
> #include <boost/process.hpp> /* boost::process::child, ipstream */
> #include <boost/chrono.hpp> /* boost::chrono::seconds */
> #include <boost/thread.hpp> /* try_join_for */
>
> #include <string> /* string */
>
> std::string Run_Command_string(std::string const &str_prog,
> unsigned const timeout = 5u) noexcept

Why not chrono here, so you don't have to wonder if it's 5 s or 5 ms?

...
> /* Retain the first line of text */
> std::getline(ip,retval);
>
> /* Now just discard the rest of the output */
> for (string tmp; std::getline(ip,tmp); ) { /* Do Nothing */ }

This means you're waiting for the whole input before you process the
first line, and the whole input may be infinite, or take days to
produce.

The Unix way to handle this is (I think) to close the pipe and wait
for the child to notice (via SIGPIPE at least) and die. I don't know
what boost::process does to support it, or what would work on Windows.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Frederick Gotham

unread,
Mar 10, 2020, 5:11:19 AM3/10/20
to
On Tuesday, March 10, 2020 at 6:55:40 AM UTC, Jorgen Grahn wrote:

> > I will give a default timeout of 5 seconds for the second program to finish.
> Why? It's an unusual feature and complicates things.


On one of the embedded-Linux devices I'm developing, you have to stop a system daemon before you can make a data request to the lens module. Sometimes it takes a few seconds for the daemon to stop, so I give it a maximum of 10 seconds before I consider it to have frozen or crashed.


> > std::string Run_Command_string(std::string const &str_prog,
> > unsigned const timeout = 5u) noexcept
>
> Why not chrono here, so you don't have to wonder if it's 5 s or 5 ms?


Yeah but wouldn't I need to turn it into a template function? Something like:

template<class Rep, class Period>
std::string Run_Command_string(std::string const &str_prog, std::chrono::duration<Rep,Period> const &timeout) noexcept


> This means you're waiting for the whole input before you process the
> first line, and the whole input may be infinite, or take days to
> produce.


That's why I've got the timeout. Actually now that I think of it, I could get the Reader thread to immediately notify the main thread when it's got the first line.

Frederick Gotham

unread,
Mar 10, 2020, 8:28:22 AM3/10/20
to
On Tuesday, March 10, 2020 at 6:55:40 AM UTC, Jorgen Grahn wrote:

> This means you're waiting for the whole input before you process the
> first line, and the whole input may be infinite, or take days to
> produce.



Here's my third attempt:


/* The next function is to run a command and capture its
first line of stdout. On failure, returns empty string. */
std::string Run_Command_string(std::string const &str_cmd, unsigned const timeout = 5u) noexcept
{
using std::string;
namespace bp = boost::process;

bp::ipstream *p_ip = nullptr;

try
{
string retval;

p_ip = new bp::ipstream; /* For capturing stdin from child process */

bp::child c(bp::search_path("sh"), "-c", str_cmd, bp::std_in.close(), bp::std_out > *p_ip, bp::std_err > bp::null);

boost::mutex mutex_for_messaging;
bool bHave_first_line_yet = false;
boost::mutex::scoped_lock lock_for_condition(mutex_for_messaging);
boost::condition_variable have_first_line_yet;

//cout << "About to start Reader thread" << endl;

/* Start second thread here to capture the first line of stdout */
boost::thread reader(
[=, p_ip, &retval, &have_first_line_yet, &bHave_first_line_yet]
{
try
{
/* Retain the first line of text */
try
{
std::getline(*p_ip,retval);
}
catch (...)
{
retval.clear();
}

bHave_first_line_yet = true;
have_first_line_yet.notify_one(); /* The main thread is doing 'wait_for' */

/* ==== DANGER DANGER DANGER ==== Run_Command_string might have returned by this point ==== DANGER ==== DANGER */
/* so do not use retval, have_first_line_yet, bHave_first_line_yet past this point */

try
{
/* Now just discard the rest of the output */
for (string tmp; std::getline(*p_ip,tmp); ) { /* Do Nothing */ }
}
catch (...)
{
/* Do Nothing */
}

delete p_ip;
}
catch (...)
{
/* Do Nothing */
}
}
);

bool const did_not_timeout = (boost::cv_status::no_timeout == have_first_line_yet.wait_for(lock_for_condition, boost::chrono::seconds(timeout)));

if ( did_not_timeout && bHave_first_line_yet )
{
/* We got the first line within the timeout! :-) */

reader.detach(); /* Allow it to continue reading lines from the child program */

//cout << "Finished in time: Reader thread" << endl;

return retval;
}
else
{
//cout << "TIMEOUT: Reader thread" << endl;

/* Program has frozen, so just return an empty string */
c.terminate();
reader.interrupt();
reader.detach();
}
}
catch (...)
{
/* Do Nothing */
}

delete p_ip; /* Is there a chance that this could be a second deletion? */

return string();
}


I have tested this on Linux with a sample program:

void Do(void)
{
static std::mutex mtx;

std::string const &retval1 = Run_Command_string("find / | grep bin");
mtx.lock(); cout << "==== ONE ==== " << retval1 << endl; mtx.unlock();

std::string const &retval2 = Run_Command_string("find / | grep usr");
mtx.lock(); cout << "==== TWO ==== " << retval2 << endl; mtx.unlock();

std::string const &retval3 = Run_Command_string("find / | grep lib");
mtx.lock(); cout << "==== TR3 ==== " << retval3 << endl; mtx.unlock();
}

#include <vector>

auto main(void) -> int
{
std::vector<std::thread*> vec;

for (unsigned i = 0; i != 45; ++i)
{
vec.push_back(new std::thread(Do));
}

for (;;) {}
}

Of course there's a few more things to take into account, such as whether the main thread should wait for all threads to finish (or to kill them) before returning from main.


Frederick Gotham

unread,
Mar 10, 2020, 8:40:36 AM3/10/20
to
On Tuesday, March 10, 2020 at 12:28:22 PM UTC, Frederick Gotham wrote:

> delete p_ip; /* Is there a chance that this could be a second deletion? */


This definitely should be there. If it should be anywhere it should be right beside the call to thread::interrupt, but even then it isn't certain that the Reader thread hasn't already deleted it.

cda...@gmail.com

unread,
Mar 10, 2020, 8:51:29 AM3/10/20
to
I think my IQ dropped 20 points after reading this crap.

Frederick Gotham

unread,
Mar 10, 2020, 9:08:04 AM3/10/20
to
On Tuesday, March 10, 2020 at 12:51:29 PM UTC, cda...@gmail.com wrote:

> I think my IQ dropped 20 points after reading this crap.


I know a guy who had his corpus callosum in his brain(s) severed so that he could think about two things independently at once without having one idea interfere with the other. (Effectively giving him two separate 'threads' of thought).

I can't tell you more about him though because it's highly illegal to practise this surgery on people for experimental purposes (even if you are indeed a competent and fully-licensed medical surgeon).



Frederick Gotham

unread,
Mar 10, 2020, 9:57:28 AM3/10/20
to
On Tuesday, March 10, 2020 at 12:28:22 PM UTC, Frederick Gotham wrote:

> Here's my third attempt:


I might be going a little far with the static array of bits to keep track of deletions, but here's my fourth attempt:

/* The next function is to run a command and capture its
first line of stdout. On failure, returns empty string. */
std::string Run_Command_string(std::string const &str_cmd, unsigned const timeout = 5u) noexcept
{
/* ====== Determine all the limits and integer types in this block ======= */
typedef uint64_t IntBits;
static std::size_t constexpr maxcalls = 1000000u; /* 1 million (must be divisable by width of IntBits) */
static uint_fast32_t counter = 0; /* 32-Bit can do up to 4 billion */
/* ================= END OF BLOCK ======================================== */

static std::size_t constexpr w = sizeof(IntBits) * CHAR_BIT; /* Assume no padding bits inside integer types */

static IntBits bits[maxcalls / w] = {}; /* Starts out all zero */

static boost::mutex mtx_bits;

decltype(counter) local_counter;

{ /* This is a deliberate scope block */
boost::mutex::scoped_lock scpl(mtx_bits);

local_counter = counter;
bits[counter / w] |= static_cast<IntBits>(1) << (counter % w);
++counter;
}

using std::string;
namespace bp = boost::process;

bp::ipstream *p_ip = nullptr;

try
{
string retval;

p_ip = new bp::ipstream; /* For capturing stdin from child process */

bp::child c(bp::search_path("sh"), "-c", str_cmd, bp::std_in.close(), bp::std_out > *p_ip, bp::std_err > bp::null);

boost::mutex mutex_for_messaging;
bool bHave_first_line_yet = false;
boost::mutex::scoped_lock lock_for_condition(mutex_for_messaging);
boost::condition_variable have_first_line_yet;

//cout << "About to start Reader thread" << endl;

/* Start second thread here to capture the first line of stdout */
boost::thread reader(
[=, p_ip, local_counter, &retval, &have_first_line_yet, &bHave_first_line_yet]
{
try
{
/* Retain the first line of text */
try
{
std::getline(*p_ip,retval);
}
catch (...)
{
retval.clear();
}

bHave_first_line_yet = true;
have_first_line_yet.notify_one(); /* The main thread is doing 'wait_for' */

/* ==== DANGER DANGER DANGER ==== Run_Command_string might have returned by this point ==== DANGER ==== DANGER */
/* so do not use retval, have_first_line_yet, bHave_first_line_yet past this point */

try
{
/* Now just discard the rest of the output */
for (string tmp; std::getline(*p_ip,tmp); ) { /* Do Nothing */ }
}
catch (...)
{
/* Do Nothing */
}

{
boost::mutex::scoped_lock scpl(mtx_bits);

IntBits const mask = static_cast<IntBits>(1) << (local_counter % w);

if ( bits[local_counter / w] & mask )
{
delete p_ip;

bits[local_counter / w] &= ~mask;
}
}
}
catch (...)
{
/* Do Nothing */
}
}
);

bool const did_not_timeout = (boost::cv_status::no_timeout == have_first_line_yet.wait_for(lock_for_condition, boost::chrono::seconds(timeout)));

if ( did_not_timeout && bHave_first_line_yet )
{
/* We got the first line within the timeout! :-) */

reader.detach(); /* Allow it to continue reading lines from the child program */

//cout << "Finished in time: Reader thread" << endl;

return retval;
}
else
{
//cout << "TIMEOUT: Reader thread" << endl;

/* Program has frozen, so just return an empty string */
c.terminate();
reader.interrupt();
reader.detach();

{ /* This is a deliberate scope block */
boost::mutex::scoped_lock scpl(mtx_bits);

IntBits const mask = static_cast<IntBits>(1) << (local_counter % w);

if ( bits[local_counter / w] & mask )
{
delete p_ip;

bits[local_counter / w] &= ~mask;
}
}
}
}
catch (...)
{
/* Do Nothing */
}

return string();
}

cda...@gmail.com

unread,
Mar 10, 2020, 10:08:12 AM3/10/20
to
Not that I should encourage this shutout, but the following line that you commented out...

//cout << "TIMEOUT: Reader thread" << endl;

Is kind of incorrect because error messages on Linux are unbuffered. And again, you would have only known his had you taken my advice and read the book "Advanced Programming in the Unix Environment" by the late Dr. Stevens.

And again, I think my IQ dropped 20 points after reading this crap.

Jorgen Grahn

unread,
Mar 11, 2020, 3:16:42 AM3/11/20
to
On Tue, 2020-03-10, Frederick Gotham wrote:
> On Tuesday, March 10, 2020 at 6:55:40 AM UTC, Jorgen Grahn wrote:
>
>> > I will give a default timeout of 5 seconds for the second program to finish.
>> Why? It's an unusual feature and complicates things.
>
>
> On one of the embedded-Linux devices I'm developing, you have to
> stop a system daemon before you can make a data request to the lens
> module. Sometimes it takes a few seconds for the daemon to stop, so
> I give it a maximum of 10 seconds before I consider it to have
> frozen or crashed.

I didn't understand that.

How would this affect the output of some command? Especially if it's
'ps aux | grep foo', which seemed to be what you intended to use the
code for.

BTW, pgrep(1) is better for such things than grepping the output of
ps(1), and seems to be installed everywhere.

>> > std::string Run_Command_string(std::string const &str_prog,
>> > unsigned const timeout = 5u) noexcept
>>
>> Why not chrono here, so you don't have to wonder if it's 5 s or 5 ms?
>
>
> Yeah but wouldn't I need to turn it into a template function? Something like:
>
> template<class Rep, class Period>
> std::string Run_Command_string(std::string const &str_prog, std::chrono::duration<Rep,Period> const &timeout) noexcept

There is something weird about chrono, yes. I haven't learned it well
enough to answer that, although I don't think you don't have to let
the template "poison" your code.

>> This means you're waiting for the whole input before you process the
>> first line, and the whole input may be infinite, or take days to
>> produce.
>
>
> That's why I've got the timeout. Actually now that I think of it, I
> could get the Reader thread to immediately notify the main thread
> when it's got the first line.

There's still a easy standard way to solve that though (the one you
snipped). Although if you risk never getting even /one/ line, that
standard way doesn't help. But like I wrote, that's an unusual
requirement.

Frederick Gotham

unread,
Mar 12, 2020, 8:30:50 AM3/12/20
to
On Monday, March 9, 2020 at 11:18:20 AM UTC, Frederick Gotham wrote:

> I want to write a robust function to start a second program and
> to retrieve the first line of text from its stdout. If any kind
> of error takes places, the function will simply return an empty
> string. No exceptions will be thrown.


Here's my fifth attempt. But first, an explanation:

* Each individual command is expected to return its first line of stdout within 2 seconds
* After all commands have returned their first line (or timed out), all outstanding threads are collectively given 8 seconds to finish, and after the 8 seconds, everything gets nuked (safely).

I'm still having a little trouble with segfaults inside the function "Handle_Cleanup", but anyway here's my fifth attempt. Oh by the way I realise I went a little crazy with keeping everything inside the function "Run_Command_string", I can of course take some stuff outside the function.

ATTEMPT FIVE:

#include <boost/process.hpp> /* boost::process::child, ipstream */
#include <boost/chrono.hpp> /* boost::chrono::seconds */
#include <boost/thread.hpp> /* try_join_for */
#include <boost/lockfree/queue.hpp> /* boost::lockfree:queue */

#include <string>

/* ===== For Testing ===== */
#include <iostream>
using std::cout;
using std::endl;
/* ======================= */

/* The next function is to run a command and capture its
first line of stdout. On failure, returns empty string. */
std::string Run_Command_string(std::string const &str_cmd, unsigned const timeout = 2u) noexcept
{
struct Thread_Manager {

struct Handle {
boost::process::ipstream *p_ip;
boost::process::child *p_child;
boost::thread *p_thread;
};

boost::lockfree::queue<Handle> ctr;

Thread_Manager(void) : ctr(600u) {} /* Pre-allocate for 600 elements */

static void Cleanup_Handle(Handle const &h)
{
/* I'm not entirely sure why but I've had
to do some strange stuff in this
function in order to prevent a segfault */

h.p_child->terminate();

delete h.p_thread;

h.p_ip->pipe().close();

boost::this_thread::sleep_for(boost::chrono::milliseconds(100u)); /* Segfault if you don't do this */

delete h.p_ip;

delete h.p_child;
}

~Thread_Manager(void)
{
cout << "==============================================\n"
" BEGIN THREAD CLEAN-UP\n"
"==============================================\n";

bool man_alive = false;

Handle h;

for ( ; ctr.pop(h); Cleanup_Handle(h) )
{
if ( nullptr == h.p_thread )
{
cout << "= = = Alpha Loop: Thread pointer is nullptr = = =\n";
continue;
}

if ( false == h.p_thread->joinable() )
{
cout << "= = = Alpha Loop: Thread object is not for an actual thread = = =\n";
continue;
}

if ( true == h.p_thread->try_join_for(boost::chrono::milliseconds(1u)) )
{
cout << "= = = Alpha Loop: Thread object is for a finished thread = = =\n";
continue;
}

/* If control reaches here, we have at least one
thread that hasn't finished yet */

cout << "= = = Alpha Loop: Thread object is for thread still running = = =\n";

man_alive = true;
break; /* Note that the three pointers haven't been deleted when we break out */
}

if ( false == man_alive )
{
/* Nothing more to do, we can return from the destructor */
cout << "= = = All threads finished. No need for amnesty. = = =\n";
return;
}

/* If control reaches here, there are still some threads running,
and the first one is pointed to by 'p' */

/* Give everything 8 seconds to finish up */

cout << "============================ BEGIN 8 SECOND AMNESTY =====================================\n";
boost::this_thread::sleep_for(boost::chrono::seconds(8u));
cout << "============================ END 8 SECOND AMNESTY =====================================\n";

/* Now just kill any threads still running */

goto Label_To_Jump_Into_Loop; /* Because the current handle is still to be deal with */

for ( ; ctr.pop(h); Cleanup_Handle(h) )
{
Label_To_Jump_Into_Loop:

if ( nullptr == h.p_thread )
{
cout << "= = = Beta Loop: Thread pointer is nullptr = = =\n";
continue;
}

if ( false == h.p_thread->joinable() )
{
cout << "= = = Beta Loop: Thread object is not for an actual thread = = =\n";
continue;
}

if ( true == h.p_thread->try_join_for(boost::chrono::milliseconds(1)) )
{
cout << "= = = Beta Loop: Thread object is for a finished thread = = =\n";
continue;
}

/* If control reaches here, thread that hasn't finished yet */

cout << "= = = Beta Loop: Killing thread that didn't stop in time = = =\n";

/* To kill a thread, you call interrupt and immediately follow
it with either "join" or "detach" */

//cout << "From Start\n";
h.p_thread->interrupt();
h.p_thread->detach();
//cout << "To Finish\n";
}

cout << "> = > = > = > = > = > = Destruction of Thread_Manager is finitio > = > = > = > = > = > =\n";

/* All threads are now dead and gone */
return;
}
};

using std::string;
namespace bp = boost::process;

try
{
static Thread_Manager mgr;

string retval;

Thread_Manager::Handle handle{new bp::ipstream, nullptr, nullptr}; /* First pointer is for capturing stdin from child process */

handle.p_child = new bp::child(bp::search_path("sh"), "-c", str_cmd, bp::std_in.close(), bp::std_out > *handle.p_ip, bp::std_err > bp::null);

boost::mutex mutex_for_messaging;
boost::atomic_flag have_first_line_yet;
boost::mutex::scoped_lock lock_for_condition(mutex_for_messaging);
boost::condition_variable have_first_line_yet_CONDITION;

//cout << "About to start Reader thread\n";

/* Start second thread here to capture the first line of stdout */

handle.p_thread = new boost::thread(
[handle, &retval, &have_first_line_yet, &have_first_line_yet_CONDITION] /* Must take 'handle' by value */
{
try
{
/* Retain the first line of text */
try
{
std::getline(*handle.p_ip,retval);
}
catch (...)
{
retval.clear();
}

have_first_line_yet.test_and_set();
have_first_line_yet_CONDITION.notify_one(); /* The main thread is doing 'wait_for' */

/* ==== DANGER DANGER DANGER ==== Run_Command_string might have returned by this point ==== DANGER ==== DANGER */
/* so do not use retval, have_first_line_yet, bHave_first_line_yet past this point */

try
{
/* Now just discard the rest of the output */
for (string tmp; std::getline(*handle.p_ip,tmp); ) { /* Do Nothing */ }
}
catch (...)
{
/* Do Nothing */
}
}
catch (...)
{
/* Do Nothing */
}
}
);

mgr.ctr.push(handle);

boost::thread &reader = *handle.p_thread;

bool const did_not_timeout = (boost::cv_status::no_timeout == have_first_line_yet_CONDITION.wait_for(lock_for_condition, boost::chrono::seconds(timeout)));

/* A few seconds might pass here */

if ( did_not_timeout && have_first_line_yet.test_and_set() )
{
/* We got the first line within the timeout! :-) */

return retval;
}
else
{
/* Program has frozen, so just return an empty string */
reader.interrupt();
/* reader.detach(); DON'T DO THIS */
handle.p_child->terminate(); /* This might get terminated a second time by Thread_Manager */
}
}
catch (...)
{
/* Do Nothing */
}

return string();
}

auto main(void) -> int
{
for (unsigned i = 0; i != 12; ++i)
{
std::string const &retval1 = Run_Command_string("find / | grep bin");

cout << "==== ONE ==== " + retval1 + "\n";

std::string const &retval2 = Run_Command_string("find / | grep usr");
cout << "==== TWO ==== " + retval2 + "\n";

std::string const &retval3 = Run_Command_string("find / | grep lib");
cout << "==== TR3 ==== " + retval3 + "\n";
}
}


Felix Palmen

unread,
Mar 12, 2020, 11:02:13 AM3/12/20
to
* Frederick Gotham <cauldwel...@gmail.com>:
> I want to write a robust function to start a second program and to
> retrieve the first line of text from its stdout. If any kind of error
> takes places, the function will simply return an empty string. No
> exceptions will be thrown.

From looking at the code, this is only for POSIX? Then what's wrong with
popen(), select() and fgets()?

Furthermore, what's the problem to be solved here? Often enough, there's
a (much) better way than reading the output of another program.

--
Dipl.-Inform. Felix Palmen <fe...@palmen-it.de> ,.//..........
{web} http://palmen-it.de {jabber} [see email] ,//palmen-it.de
{pgp public key} http://palmen-it.de/pub.txt // """""""""""
{pgp fingerprint} A891 3D55 5F2E 3A74 3965 B997 3EF2 8B0A BC02 DA2A

Frederick Gotham

unread,
Mar 12, 2020, 11:26:09 AM3/12/20
to
On Thursday, March 12, 2020 at 3:02:13 PM UTC, Felix Palmen wrote:

> From looking at the code, this is only for POSIX? Then what's wrong with
> popen(), select() and fgets()?



Did you assume it's POSIX because I use "ps" and "grep"?



> Furthermore, what's the problem to be solved here? Often enough, there's
> a (much) better way than reading the output of another program.



In my work there's a guy who has written Linux shell script files that are hundreds of lines long. I think Linux shell scripting is fine, I use it to write about 20 or 30 lines, but for anything more complicated I'll do it in C or C++.

Anyway... I'm sort of half-porting one of his insanely long scripts to C++, and there's loads of places where he takes the output from another program. In the future I might look at cleaner ways of achieving my objectives, but for right now I'm just gonna run these other programs and collect their first line of output.

In the sample code in my previous post, I was just using "find / | grep bin" as an example of a command that gives its first line quite quickly but can then take minutes to spit out all the rest of its lines.

I'm working on embedded Linux right now (arm 32-bit) but I'd like to be able to write this with Boost so that it works on lots of systems.

I'm not entirely sure why I'm getting segfaults in "Cleanup_Handle".

Keith Thompson

unread,
Mar 12, 2020, 1:47:14 PM3/12/20
to
Frederick Gotham <cauldwel...@gmail.com> writes:
> On Thursday, March 12, 2020 at 3:02:13 PM UTC, Felix Palmen wrote:
>> From looking at the code, this is only for POSIX? Then what's wrong with
>> popen(), select() and fgets()?
>
> Did you assume it's POSIX because I use "ps" and "grep"?

Or you could have just answered the question. Is it only for POSIX?

[...]

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

cda...@gmail.com

unread,
Mar 12, 2020, 3:07:11 PM3/12/20
to
Give it up. Let it go. Move on.

Jorgen Grahn

unread,
Mar 13, 2020, 3:13:40 AM3/13/20
to
On Thu, 2020-03-12, Keith Thompson wrote:
> Frederick Gotham <cauldwel...@gmail.com> writes:
>> On Thursday, March 12, 2020 at 3:02:13 PM UTC, Felix Palmen wrote:
>>> From looking at the code, this is only for POSIX? Then what's wrong with
>>> popen(), select() and fgets()?
>>
>> Did you assume it's POSIX because I use "ps" and "grep"?
>
> Or you could have just answered the question. Is it only for POSIX?

He did, further down:

I'm working on embedded Linux right now (arm 32-bit) but I'd like
to be able to write this with Boost so that it works on lots of
systems.

Surely these "lots of systems" boil down to Windows -- I doubt there
are other non-POSIX systems where this Boost library works.

James Kuyper

unread,
Mar 13, 2020, 7:25:05 AM3/13/20
to
On 3/12/20 11:25 AM, Frederick Gotham wrote:
> On Thursday, March 12, 2020 at 3:02:13 PM UTC, Felix Palmen wrote:
>
>> From looking at the code, this is only for POSIX? Then what's wrong with
>> popen(), select() and fgets()?
>
>
>
> Did you assume it's POSIX because I use "ps" and "grep"?

That's a reasonable assumption. "find" is also a clue, but explicit
mention of Linux is the easiest clue to follow.

I notice that, even with Keith prompting you to do so, you still haven't
bothered answering that question.

If the answer is "Yes", then popen(), select(), and fgets() together
make a much simpler solution to your problem then the one you've
created. And because it's simpler, it should be much easier to get it
actually working.

If the answer is "No", then could you explain what kind of non-POSIX
environment supports commands named "ps", "grep", and "find", and can be
accurately described as Linux?

cda...@gmail.com

unread,
Mar 13, 2020, 11:42:32 AM3/13/20
to
Not that I'm the programming grandmaster, but I have done some kind of programming on embedded Linux (arm 32 -bit). And in general, to get it to work on a lot of systems is vague. For example, if this were android, in order to get it to work on a lot of systems, he would have Java/XML and these have different builds for that target the minimum API levels.

If he wanted to wanted to this to also work on say, an iPhone, he would either have to use swift and maintain a few different builds. Otherwise, he would have to use HookerScript, I mean JavaScript, and then do some serious optimizations in order to get JavaScript to perform well as the native APIS on both Android and iPhone.

Jorgen Grahn

unread,
Mar 14, 2020, 12:54:48 PM3/14/20
to
On Thu, 2020-03-12, Frederick Gotham wrote:
> On Thursday, March 12, 2020 at 3:02:13 PM UTC, Felix Palmen wrote:
...

>> Furthermore, what's the problem to be solved here? Often enough, there's
>> a (much) better way than reading the output of another program.
>
> In my work there's a guy who has written Linux shell script files
> that are hundreds of lines long. I think Linux shell scripting is
> fine, I use it to write about 20 or 30 lines, but for anything more
> complicated I'll do it in C or C++.
>
> Anyway... I'm sort of half-porting one of his insanely long scripts
> to C++, and there's loads of places where he takes the output from
> another program. In the future I might look at cleaner ways of
> achieving my objectives, but for right now I'm just gonna run these
> other programs and collect their first line of output.

This strikes me as a bad idea.

Shell scripting languages are designed to tie commands together, with
streams and files, and pipelines. For that kind of task it's a much
better tool than C++.

Even straight ports of shell scripts to Perl or Python is a bad
idea, IMO.

Things change if the problem isn't really a shell kind of problem, and
you do a brand new solution in C++. But the 'ps aux | grep foo' makes
this sound unlikely.

cda...@gmail.com

unread,
Mar 14, 2020, 2:58:31 PM3/14/20
to
Here comes something the OP clearly doesn't know about. The output on *nix is divided into three major categories. Line buffered, unbuffered, and fully buffered. And if I saw the original shell scripts, I'm wiling to bet that the author took these three different cases into account because the OPs code clearly doesn't.

This despite the fact that I've told this halfwit that he should give the computer a rest and read "Advanced Programming in the Unix Environment" by the late Dr. Stevens. Seriously. If this dispshit would have taken my advice and read this book, he would know what I'm talking about and then maybe see why rewriting this stuff in C++ is sometimes a bad idea.

But I guess it's easier just to act like a retarded on the internet.

Uhh…

Hi mom!

0 new messages