Load the output of another command line program into mine - c++

Ok suppose I have a program ( in windows a .exe file ) and when I run it, it outputs some information... now I'm writing another program ( in c++ ) and I need it to automatically run that .exe file and read the output so that it can process that information for further actions...
what should I do ?

Use popen or on windows (per comment) _popen. Basically it functions as the thing behind the | in some program | thing.
Normally I'm against posting complete code but I literally wrote this today and have it on hand, so, here you go. From what I understand C++ doesn't have a great interface that replaces popen but if you're bringing in the boost libraries or something at that layer there are solutions.
Note I use char[10] because in my application I know the output will be that short.
PopenWrapper(const std::string& command) {
fd = popen(command.c_str(), "r");
if(fd == NULL) {
throw PopenException("Failed to open command: " + command);
}
}
std::string get() {
char line[10];
fgets(line, sizeof(line), fd);
return std::string(line);
}
~PopenWrapper() {
if(fd != NULL) {
pclose(fd);
}
}

Related

Execute command from cpp

The application I'm working on needs to execute commands. Commands can be console commands or 'GUI applications' (like notepad).
I need to get the return code in both cases, and in the case of console commands I also need to catch the output from stdin and stderr.
In order to implement this feature, I based my code on the stack overflow question 'How to execute a command and get output of command within C++ using POSIX?'.
My code:
int ExecuteCmdEx(const char* cmd, std::string &result)
{
char buffer[128];
int retCode = -1; // -1 if error ocurs.
std::string command(cmd);
command.append(" 2>&1"); // also redirect stderr to stdout
result = "";
FILE* pipe = _popen(command.c_str(), "r");
if (pipe != NULL) {
try {
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
}
catch (...) {
retCode = _pclose(pipe);
throw;
}
retCode = _pclose(pipe);
}
return retCode;
}
It works perfectly with console applications, but in the case of 'GUI applications' it doesn't work as expected...
With 'GUI applications', code stops on while (!feof(pipe)) expecting to get something from pipe.
I understand that 'GUI applications' like notepad don't finish until someone interacts with them (user closes the app, kills the process, etc.),
but when I launch console applications from Windows Console, prompt comes back immediately.
I would like to obtain the same behavior from 'GUI applications'...
One possible solution would be to add the isGui variable indicating when the code should read from the pipe, but I rejected this option, as I don't want to indicate if it is a 'GUI application' or not.
Well you don't have to indicate isGui yourself but detect it by checking the subsystem of the executable (windows/console) prior to executing the command, and in case of windows skip waiting on the redirected pipes.
For example, using SHGetFileInfo with the SHGFI_EXETYPE flag:
bool isGuiApplication(const std::string& command)
{
auto it = command.find_first_of(" \t");
const std::string& executable = (it == std::string::npos ? command : command.substr(0, it));
DWORD_PTR exetype = SHGetFileInfo(executable.c_str(), 0, nullptr, 0, SHGFI_EXETYPE);
if (!exetype) {
cerr << "Executable check failed\n";
}
return ((uintptr_t)exetype & 0xffff0000);
}
Then later in the code...
if (isGuiApplication(command)) {
cout << "GUI application\n";
system(command.c_str()); // don't wait on stdin
}
else {
cout << "Console application\n";
. . .
// _popen and stuff
}

tar command in execl using busybox. Error: no such file or directory

I have a linux based device that runs c++ code using QT framework. Using QProcess is not an option, since we don't have the QT compiled to support it.
I can't create a tar.gz archive using execl().
It returns -1(fail) and error is "No such file or directory"
Code sample:
std::string applicationPathWithName = "/bin/busybox";
QString dataDirectory("/opt/appl/data/");
QString archiveName = QString("AswLogs.tar.gz");
char* applName;
applName = new char [applicationPathWithName.size() + 1];
strcpy(applName, applicationPathWithName.c_str());
itsFlmFileManagerPtr->writeInFile(eFlmFileTypes_LogFile, data); //This creates logs.txt successfully
pid_t pid = fork();
QString command = QString("tar -czvf %1%2 %3logs.txt").arg(dataDirectory).arg(archiveName).arg(dataDirectory);
if(0 == pid)
{
INFO("Pid is 0");
int execStatus = 0;
execStatus = execl(applName, applName, command.toStdString().c_str(), (char*)NULL);
INFO("Execl is done, execStatus= " << execStatus);
std::string errorStr = strerror(errno);
INFO("Error: " << errorStr);
_exit(EXIT_FAILURE);
}
else if (pid < 0)
{
INFO("Failed to fork");
}
else
{
INFO("pid=" << pid);
int status;
if(wait(&status) == -1)
{
INFO("Wait child error");
}
INFO("Resume from fork");
}
Output:
pid=877
Pid is 0
Execl is done, execStatus= -1
Error: No such file or directory
Resume from fork
Permissions:
logs.txt 666 |
busybox 755
How can I get more error details or what is wrong here?
Edit:
So, after a while, I tried to do just the .tar archive and it worked.
Then I tried just to do the .gz compression and it also worked.
Solution:
So, at least in my case, the solution was to do the tar.gz in two steps(Two processes required):
execl("/bin/busybox", "/bin/busybox", "tar", "-cvf", "/opt/appl/data/logs.tar", "/opt/appl/data/logs.txt", (char*) NULL);
execl("/bin/busybox", "/bin/busybox", "gzip", "/opt/appl/data/logs.tar", (char*) NULL);
I don't know what platform or compiler this is, but it generally isn't possible to pass whole command lines to execl(). If I understanding correctly, you are running something like this:
execl ("/bin/busybox", "/bin/busybox", "tar -czvf blah blah", null);
but in general you need
execl ("/bin/busybox", "/bin/busybox", "tar", "-czvf", "blah", "blah", null);
That is, you need to parse the command line down to its individual arguments. That should be easy enough in the case you described, since you already know what the individual arguments are.
I think the problem is that /bin/busybox starts, but chokes when it tries to interpret "tar -czvf blah blah" as the name of an applet to run.
Incidentally -- and probably not related -- busybox "tar" won't handle gzip compression internally by default, unless you have enabled this feature at build time.

Get Gnuplot version from pipe in C++

In my C++ program (in linux), I can open a pipe for writing and set values for Gnuplot program.
FILE *pipe = NULL;
#ifdef WIN32
pipe = _popen("pgnuplot -persist", "w");
#else
pipe = popen("gnuplot", "w");
#endif
if(pipe == NULL)
error("Could not open pipe for write!");
// set title name
fprintf(pipe, "set title 'Sample Points' \n");
Now I need to get the Gnuplot version. The show version command does this but how I can send this command and then read the value. Opening a pipe for reading seems to not work for me and the code stuck in the while loop without getting any data.
FILE* pipe = popen(command, "r");
if (!pipe)
{
std::cout << "failed! (can not open pipe)" << endl;
return;
}
char buffer[128];
std::string result = "";
while(!feof(pipe))
{
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
Since on my Debian/Linux/Sid/x86-64 the command gnuplot --version is outputting to stdout the following line:
gnuplot 5.0 patchlevel 1
I would simply recommend
FILE* pipversion = popen("gnuplot --version", "r");
if (!pipversion) { perror("popen gnuplot"); exit(EXIT_FAILURE); };
char lineversion[128];
memset (lineversion, 0, sizeof(lineversion));
if (!fgets(lineversion, sizeof(lineversion), pipversion) {
perror("fgets"); exit(EXIT_FAILURE);
}
/// lineversion is like: gnuplot 5.0 patchlevel 1
int majvers=0, minvers=0, pos= -1;
char* restvers = NULL;
if (sscanf(lineversion, "gnuplot %d.%d %n", &majvers, &minvers, &pos) >= 2) {
assert (pos>=0);
restvers = lineversion+pos;
};
pclose(pipversion);
pipversion = NULL;
After that, majvers contains the major version of gnuplot (e.g. 5 in my case) and minvers contains the minor version (e.g. 0), with restvers being a suffix string (e.g. "patchlevel 1" without the quotes).
There might be a potential race condition in the unusual and unlikely case that gnuplot is updated between this popen and the next one pipe = popen("gnuplot", "w");. BTW, naming a variable pipe is poor taste, since POSIX and Linux have the pipe(2) system call. But I don't think it is worth caring about that race condition.
BTW, you very probably want to replace your second pipe = popen("gnuplot", "w"); with an explicit double invocation of pipe(2) (followed by appropriate fork(2) & execvp(3) ...) to have both input and output pipes to gnuplot, and manage them in your own event loop (probably around poll(2) ... see this & that answers).
(if you application has or uses its own event loop, in particular if it is a GUI application above Qt or GTK, you want to use the same event loop for the pipes; details are specific to the library providing that event loop: g_spawn_async_with_pipes & g_source_add_unix_fd for GTK, QProcess for Qt ... )
I don't have time to explain how to do that (double piping into command + event loop) in details, but the Advanced Linux Programming book (available online) has several chapters on that. Be aware that you need some event loop.

Creating text file into C++ addon of node.js

I want to know how i can create file and append data inside it in c++ addon (.cc) file of node.js ??
I have used below code to do same, but not able to find file "data.txt" in my ubuntu machine(reason behind it may be below code is not correct way to create file, but strange i haven't received any error/warning at compile time).
FILE * pFileTXT;
pFileTXT = fopen ("data.txt","a+");
const char * c = localReq->strResponse.c_str();
fprintf(pFileTXT,c);
fclose (pFileTXT);
Node.js relies on libuv, a C library to handle the I/O (asynchronous or not). This allows you to use the event loop.
You'd be interested in this free online book/introduction to libuv: http://nikhilm.github.com/uvbook/index.html
Specifically, there is a chapter dedicated to reading/writing files.
int main(int argc, char **argv) {
// Open the file in write-only and execute the "on_open" callback when it's ready
uv_fs_open(uv_default_loop(), &open_req, argv[1], O_WRONLY, 0, on_open);
// Run the event loop.
uv_run(uv_default_loop());
return 0;
}
// on_open callback called when the file is opened
void on_open(uv_fs_t *req) {
if (req->result != -1) {
// Specify the on_write callback "on_write" as last argument
uv_fs_write(uv_default_loop(), &write_req, 1, buffer, req->result, -1, on_write);
}
else {
fprintf(stderr, "error opening file: %d\n", req->errorno);
}
// Don't forget to cleanup
uv_fs_req_cleanup(req);
}
void on_write(uv_fs_t *req) {
uv_fs_req_cleanup(req);
if (req->result < 0) {
fprintf(stderr, "Write error: %s\n", uv_strerror(uv_last_error(uv_default_loop())));
}
else {
// Close the handle once you're done with it
uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
}
}
Spend some time reading the book if you want to write C++ for node.js. It's worth it.

Unable to capture standard output of process using Boost.Process

Currently am using Boost.Process from the Boost sandbox, and am having issues getting it to capture my standard output properly; wondering if someone can give me a second pair of eyeballs into what I might be doing wrong.
I'm trying to take thumbnails out of RAW camera images using DCRAW (latest version), and capture them for conversion to QT QImage's.
The process launch function:
namespace bf = ::boost::filesystem;
namespace bp = ::boost::process;
QImage DCRawInterface::convertRawImage(string path) {
// commandline: dcraw -e -c <srcfile> -> piped to stdout.
if ( bf::exists( path ) ) {
std::string exec = "bin\\dcraw.exe";
std::vector<std::string> args;
args.push_back("-v");
args.push_back("-c");
args.push_back("-e");
args.push_back(path);
bp::context ctx;
ctx.stdout_behavior = bp::capture_stream();
bp::child c = bp::launch(exec, args, ctx);
bp::pistream &is = c.get_stdout();
ofstream output("C:\\temp\\testcfk.jpg");
streamcopy(is, output);
}
return (NULL);
}
inline void streamcopy(std::istream& input, std::ostream& out) {
char buffer[4096];
int i = 0;
while (!input.eof() ) {
memset(buffer, 0, sizeof(buffer));
int bytes = input.readsome(buffer, sizeof buffer);
out.write(buffer, bytes);
i++;
}
}
Invoking the converter:
DCRawInterface DcRaw;
DcRaw.convertRawImage("test/CFK_2439.NEF");
The goal is to simply verify that I can copy the input stream to an output file.
Currently, if I comment out the following line:
args.push_back("-c");
then the thumbnail is written by DCRAW to the source directory with a name of CFK_2439.thumb.jpg, which proves to me that the process is getting invoked with the right arguments. What's not happening is connecting to the output pipe properly.
FWIW: I'm performing this test on Windows XP under Eclipse 3.5/Latest MingW (GCC 4.4).
[UPDATE]
From debugging, it would appear that by the time the code reaches streamcopy, the file/pipe is already closed - bytes = input.readsome(...) is never any value other than 0.
Well I think that you need to redirect correctly the output stream. In my application something like this works :
[...]
bp::command_line cl(_commandLine);
bp::launcher l;
l.set_stdout_behavior(bp::redirect_stream);
l.set_stdin_behavior(bp::redirect_stream);
l.set_merge_out_err(true);
bp::child c = l.start(cl);
bp::pistream& is = c.get_stdout();
string result;
string line;
while (std::getline(is, line) && !_isStopped)
{
result += line;
}
c.wait();
[...]
Without the redirect the stdout will go nowhere if I remember correctly. It is a good practice to wait for the process end if you want to get the whole output.
EDIT:
I'm on Linux with perhaps an old version of boost.process. i realize that your code is similar to the snippet I gave you. The c.wait() might be the key ...
EDIT: Boost.process 0.1 :-)
If migrating to the "latest" boost.process isn't an issue (as you sure know, there are several variants for this library) ,you could use the following (http://www.highscore.de/boost/process0.5/)
file_descriptor_sink sink("stdout.txt");
execute(
run_exe("test.exe"),
bind_stdout(sink)
);