Running a Bash Script with arguments as a Qt resource - c++

Qt newbie here :).
I'm currently executing a bash script in Qt using the popen function to redirect the output to a textBrowser in my application. When I add the script to my project as a resource it does not seem to execute anymore? I use the :/myScript.sh syntax and then try to add my arguments as QStrings.
Any advice will be appreciated!
FILE *in;
char buff[512];
QString args = ":/myScript.sh" + <arguments>;
QByteArray ba = args.toLatin1();
char *temp = ba.data();
if(!(in = popen(temp , "r")))
{
exit(1);
}
while(fgets(buff, sizeof(buff), in)!=NULL)
{
ui->txtConsole->append(buff);
}
ui->progressBar->setValue(50); pclose(in);

Invoking popen with the Qt resource path format will not do what you expect.
You are effectively trying to invoke is this:
popen(":/myScript.sh args", "r");
The popen function doesn't know anything about the Qt resource system nor the :/ syntax. It expects the first parameter to a path on disk that the operating system understands.
Two choices:
Just ship the myScript.sh file as a separate file and execute it directly. (What you observed as working before you tried to make the script a resource). If you aren't using compiled resources, chances are it already is a disk file. Just invoke popen on the absolute path to the file instead of with the :/ syntax.
Write code to extract the myScript.sh text file from the resources and save it locally to disk. Then invoke popen on that saved file.

Its running script file as a qt resource. You add arguments maybe this run.
QStringList arg("-c");
QFile file(":/scriptFile.sh");
arg << file.readAll();
process->start("sh", arg);

Related

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.

Read output of linux command without using temp file

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)

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.

Use a personal program in Firebreath

I'm building a plugin using Firebreath. I made a personal method in ABCPluginAPI.cpp called exe_program() and I would like to call another program using popen called my_program. All the files are into firebreath/projects/ABCPlugin/.
My method is:
string ABCPluginAPI::exe_program()
{
FILE * pPipe;
fd_set readfd;
char buff[1024];
char command[128];
int ret;
strcpy(command, "my_program");
if (!(pPipe = popen(command, "r"))) {
// Problem to execute the command
return "failed";
}
while(fgets(buff, sizeof(buff), pPipe)!=NULL){
cout << buff;
return buff;
}
}
The problem I have is that the plugin is not running my_program, actually if I execute the pwd command, it shows my $HOME directory. pwd works because is a general command but I don't want to put my program into $PATH variable because this plugin must be portable.
Probably Firebreath use a special directory to refer to this kind of files or something similar.
You probably need to specify a full path and filename of the application you want to run; the current working directory is not garanteed to always be the same value.
From the Tips and Tricks page of firebreath.org there is code you can add to your PluginCore-derived object that will give you the full path and filename of your plugin file:
// From inside your Plugin class (that extends PluginCore)
std::string MyPlugin::getFilesystemPath()
{
return m_filesystemPath;
}
You can take that path, strip off the last part, and change it to your executable filename; as long as you place the executable in the same directory as your plugin that should work fine. Alternately you could install it in some other well-known location.
Note that to call a method on your main Plugin object from your JSAPI object there should be a helper method getPlugin() on your JSAPI object (if you used fbgen to generate it):
std::string pluginPath = getPlugin()->getFilesystemPath();
Hope that helps

Bash input/output in C++

I'm writing program in C++ (for XAMPP communication) and I want to execute command which I have in strings (I know that this is simply system("command")) but I want to get the output from bash to C++ to string. I've founded several threads about this, but no which solved Bash -> C++.
You can call the FILE *popen(const char *command, const char *mode) function. Then, you can read the file it returns to get the output of your call.
It's like using a pipe to redirect the output of the command you used to a file in the hard drive and then read the file, but you don't get to create a file in the hard drive.
The documentation of the popen() is here.
You need to call the popen function, and read the output from the FILE it returns.
You can try Standard Output Redirection to redirect the standard output to a file stream
and then use it to read to a string.
Dup()