Read output of linux command without using temp file - c++

I need to read input of some linux commands to a QString variable. At first, I redirect the stream to a temp file and read from it.
However, I don't like this way. I want to avoid access the hard drive, the less the better.
I try to use the environment variable as the "temp file", then use getenv to get it into a variable.
like this:
QString query="a=$(fdisk -l)";
system(a.toStdString().c_str());
...
char* env;
env= getenv ("a");
however, I get nothing. Add export to the query has the same result.
I manually check the variables by env command. Nothing changed!
So how to fix this? And are there any better way to do this?
Any ideas are appreciated :)
PS: I want to keep the format too, it should keep \t, \n...

If you are using Qt then you should make it in a Qt's fashion, by utilizing QProcess class.
#include <QProcess>
QString command = "/usr/bin/fdisk";
QStringList arguments;
arguments << "-l";
QProcess process;
process.start(command, arguments);
process.waitForFinished(-1);
QByteArray rawOutput = process.readAllStandardOutput();
QStringList output = QString(rawOutput).split("\n");
foreach (QString line, output)
{
// do something with each line from output
}

It cannot work as you wish, because system or popen will start using fork its own shell process, and the a variable is not exported, and even if it was, it will be available only in that shell process and not in the invoking original process.
Remember that changes in child processes cannot affect their parent process in general (except by using some explicit IPC such as pipes between the two).
The only way you could do what your want is to use popen (or some QProcess specific mechanism to read on pipe the stdout of the child command) on fdisk -l then (if you absolutely want to have your getenv later working) use putenv to set your environment variable.
BTW, fdisk is probably using /proc to get its information (which you could get directly, see proc(5) ...).
Read Advanced Linux Programming, and about fork(2) and execve(2)

Related

Handle different application using C++

Is there any way to control other windows application using c++/qt?
I have: 1000 files of specific image format and an application that can open them. This application can use "Save as..." function to save these files in ".JPEG" format one by one. I want to do this automatically.
Is there any technique to do this? Thank you in advance!
Using QT you have the possibility to run a separate process using a QProcess instance.
Specifically suppose that your external application accepts input parameters (e.g. the filepath to load and the filepath where to store the result.
QProcess shell;
QStringList argv;
//[setup argument-list]
shell.setProcessChannelMode(QProcess::MergedChannels);
shell.start(commandPath, argv);
shell.waitForFinished();
Notice that QProcess can be uses as an IO stream. This is useful to interact with the process (e.g. to retrieve progress information):
shell.start(commandPath, argv);
shell.waitForReadyRead();
while(shell.state() == QProcess::Running || !shell.atEnd()){
auto s = shell.readLine()
//[do something with the current line of the process output]
}
QProcess::ExitStatus es = shell.exitStatus () ;
Of course the external process must accept input parameters and provide feedback through its standard output to solve your problem.

Will File I/O In Current Working Directory Ever Fail?

On my home Linux laptop, I like to write wrapper programs and GUI helpers for things I use frequently. However, I don't like Bash scripting very much, so I do a lot of stuff in C++. However, a lot of times, this requires me to use the system() function from the cstdlib.
This system() command is awesome, but I wanted a way to call system() and receive the stdout/stderror. The system() command only returns the return code from the command. So, in a Bash script, one can do:
myVar=$(ls -a | grep 'search string')
echo $myVar
and myVar will output whatever the stdout was for the command. So I began writing a wrapper class that will add a pipe-to-file to the end of the command, open the file, read all of the piped stdout, and return it as either one long string or as a vector of strings. The intricacies of the class are not really relevant here (I don't think anyway), but the above example would be done like this:
SystemCommand systemCommand;
systemCommand.setCommand("ls -a | grep \'search string\' ");
systemCommand.execute();
std::cout << systemCommand.outputAsString() << std::endl;
Behind the scenes, when systemCommand.execute() is called, the class ensures that the command will properly pipe all stdout/stderr to a randomly generated filename, in the current working directory. So for example, the above command would end up being
"ls -a | grep 'search string' >> 1452-24566.txt 2>&1".
The class then goes attempts to open and read from that file, using ifstream:
std::ifstream readFromFile;
readFromFile.open(_outputFilename);
if (readFromFile.is_open()) {
//Read all contents of file into class member vector
...
readFromFile.close();
//Remove temporary file
...
} else {
//Handle read failure
}
So here is my main question will std::ifstream ever fail to open a recently created file in the current working directory? If so, what would be a way to make it more robust (specifically on Linux)?
A side/secondary question: Is there a very simplified way to achieve what I'm trying to achieve without using file pipes? Perhaps some stuff available in unistd.h? Thanks for your time.
So here is my main question will std::ifstream ever fail to open a recently created file in the current working directory?
Yes.
Mount a USB thumb drive (or some other removable media)
cd to the mount
Execute your program. While it's executing, remove the drive.
Watch the IO error happen.
There's a ton of other reasons too. Filesystem corruption, hitting the file descriptor limit, etc.
If so, what would be a way to make it more robust (specifically on Linux)?
Make temporary files in /tmp, whose entire purpose is for temporary files. Or don't create a file at all, and use pipes for communication instead (Like what popen does, like harmic suggested). Even so, there are no guarantees; try to gracefully handle errors.

C++ (Qt), System command using memory buffers instead of redirection

I would like to know how to use memory buffers as the io streams to a system command using Qt.
Normally you would do something pseudocode like:
Exec Command(" command < inputfile > outputfile");
but I would like to do the entire operation in memory.
I would prefer Something psudocode like:
ByteArray input;
ByteArray output;
Exec Command("command name", &input, &output);
A specific reference, example or link to the answer would be awesome. I just need a starting spot, I think.
Thanks in advance.
One way to do that would be to create memory-mapped input and output files, and specify full path to input and output files in regular shell command - this way it will effectively be in memory.
You can create/access those programmatically, take a look at
Streaming from memory mapped files in Qt
I found out you can use a "shared memory" space and use it like a file reference. Once you have the file reference, you can use redirection with it. This is one solution anyway.

Is it safe to use a QTemporaryFile with a QProcess?

I have to read a script from the user and call a QProcess passing that script as a file.
For example, the user insert this, say, Python script
import sys
print(sys.copyright)
and I have to put that script in a file, and call the python interpreter using that file.
I thought to use a QTemporaryFile, because that file will serve just when launching the process, and I have no need to keep it open.
The question is: is it safe to open a QTemporaryFile, write something in it, pass that file to a process (which will continue indefinitely) and then destroy the temporary file? What if the process will need that file again? What if the process keep the file open?
I reckon that, if kept open by the process, no problem will arise: probably the QTemporaryFile will unlink the path, but data will still be accessible in memory.
But what if the process will try to open the file again?
Here a snippet as example (wrote on the fly)
QString script = QInputDialog::getText(blah);
QTemporaryFile tmp;
if (tmp.open()) {
tmp.write(script.toUtf8());
QStringList params;
params << tmp.fileName();
QProcess *proc = new QProcess("/usr/bin/python3");
proc->start(params);
}
As I understand it, in the case of the 'autoRemove' flag (which is on by default), the QTemporaryFile will exist so long as the instance of QTemporaryFile exists. Therefore, in the code you originally presented, when tmp goes out of scope, the file will be removed. Calling open / close on the object will not delete the file.
You could dynamically allocate the file with QTemporaryFile* pTmp = new QTemporaryFile and then delete it later, if you know when the python script has finished with it.
Ouch, I just noted the autoRemove flag in the QTemporaryFile. I guess this could be a solution: if set to false, the file will not be removed from the disk, so the process is free to reuse the file - I think.
Temporary files should be stored in system's default location, so I guess that the files are not removed until a reboot (at least, I believe Linux works this way).
This is just an idea, but I will wait for other answers or confirmations.

launch app, capture stdout and stderr in c++

How do I launch an app and capture the output via stdout and maybe stderr?
I am writing an automated build system and I need to capture the output to analyze. I'd like to update the svn repo and grab the revision number so I can move the files in autobuild/revNumber/ if successful. I also would like to build using make and upload the compile text to my server for everyone to see the warnings and errors on a failed build.
I can't find the system() function, but I found the CreateProcess() function on MSDN. I am able to launch what I need but I have no idea how to capture the stderr and stdout. I notice the process launches separately unless I set a breakpoint and keep my app exiting which it then will keep all the text in my app console window. I would also want to wait until all processes are finished and then scan the data it produced to do any additional operations I need. How do I do any of this?
In real shells (meaning, not sea shells - I mean, not in C Shell or its derivatives), then:
program arg1 arg2 >/tmp/log.file 2>&1
This runs program with the given arguments, and redirects the stdout to /tmp/log.file; the notation (hieroglyph) '2>&1' at the end sends stderr (file descriptor 2) to the same place that stdout (file descriptor 1) is going. Note that the sequence of operations is important; if you reverse them, then standard error will go to where standard output was going, and then standard output (but not standard error) will be redirected to the file.
The choice of file name shown is abysmal for numerous reasons - you should allow the user to choose the directory, and probably should include the process ID or time stamp in the file name.
LOG=${TMPDIR:-/tmp}/log.$$.$(date +%Y%m%d-%H%M%S)
program arg1 arg2 >$LOG 2>&1
In C++, you can use the system() function (inherited from C) to run processes. If you need to know the file name in the C++ program (plausible), then generate the name in the program (strftime() is your friend) and create the command string with that file name.
(Strictly, you also need getenv() to get $TMPDIR, and the POSIX function getpid() to get the process ID, and then you can simulate the two-line shell script (though the PID used would be of the C++ program, not the launched shell).
You could instead use the POSIX popen() function; you'd have to include the '2>&1' notation in the command string that you create to send the standard error of the command to the same place as standard output goes, but you would not need a temporary file:
FILE *pp = popen("program arg1 arg2 2>&1", "r");
You can then read off the file stream. I'm not sure whether there's a clean way to map a C file stream into a C++ istream; there probably is.
You need to fill up the STARTUP_INFO structure, which has hStdInput, hStdOutput and hStdError. Remember to inherit handles when you CreateProcess.
/* Assume you open a file handle or pipe called myoutput */
STARTUP_INFO si_startinfo;
ZeroMemory(&si_startinfo, sizeof(STARTUP_INFO));
si_startinfo.cb = sizeof(STARTUP_INFO);
si_startinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si_startinfo.hStdOutput = myoutput;
si_startinfo.hStdError = myoutput;
si_startifno.dwFlags != STARTF_USEHANDLES;
PROCESS_INFORMATION pi_procinfo;
ZeroMemory(&pi_procinfo, sizeof(PROCESS_INFORMATION);
CreateProcess(NULL, cmdline, NULL, NULL, true, 0, NULL, pathname, &si_startinfo, &pi_procinfo);
I have not shown the error handling aspects, which you will need to do. The 5th argument is set to true to inherit the handles. Others have explained how to create pipes so I won't repeat it here.
Microsoft's CRTs and the MSDN library do include the system function and the _popen function.