How to use the bash command 'which' from QProcess - c++

I'm a student programmer using Qt and I seem to have ran into an issue using QProcess to launch the bash command 'which' in an attempt to collect a map of installations of an application. I have the following code and I'm truly lost as to what I might be missing. I have referenced the QProcess documentation and still cant figure out whats wrong.
Every time this code is ran the file is not created in the indicated directory. Without the file constructed the application cannot proceed.
//datatypes
QProcess *findFiles = new QProcess();
QStringList arguments;
QStringList InstallationList;
QString program = "/bin/bash";
QString currentUsersHomeDirectory = QDir::homePath();
QString tmpScriptLocation = currentUsersHomeDirectory;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
//generate file with list of files found
tmpScriptLocation += ".whichBAScriptOutput";
arguments << QString(QString("which -a certainFile >> ") += tmpScriptLocation);
findFiles->setProcessEnvironment(env);
findFiles->start(program,arguments);
findFiles->waitForFinished();

which is located on /usr/bin/ so try to change the path..
EDIT:
You need to connect QProcess's signal readyReadStandardOutput() to your slot. Actually if you take a look at the documentation QProcess inherits from QIODevice. This means you can do something like:
while(canReadLine()){
string line = readLine();
...
}
if you have already written a client-server application in Qt, i am sure you reconized the pseudocode..

As you say you want to execute which, but you are lauching bash with a handwritten script. There is a much easier way to do this in a sequential manner:
//preparing the job,
QProcess process;
QString processName = "which"; //or absoute path if not in path
QStringList arguments = QStringList() << "-a"
<< "certainFile.txt";
// process.setWorkingDirectory() //if you want it to execute in a specific directory
//start the process
process.start(processName, arguments );
//sit back and wait
process.waitForStarted(); //blocking, return bool
process.waitForFinished(); //blocking, return bool
if(process.exitCode() != 0){
//Something went wrong
}
//return a byte array containing what the command "which" print in console
QByteArray processOutput = process.readAll();

Related

Ways to execute system commands in qt program without opening console window

I am running system() to move files in qt. This leads to blinking of console windows, Is there any way to stop the opening and closing (blinking) of console windows or any alternative ways for system() to hide console windows.
buffer = QString("move \"%2\\*.ico\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
qPrintable(buffer);
system(qPrintable(buffer));
buffer = QString("move \"%2\\*.jpg\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
system(qPrintable(buffer));
buffer = QString("move \"%2\\*.jpeg\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
system(qPrintable(buffer));
buffer = QString("move \"%2\\*.png\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
system(qPrintable(buffer));
Here %2 is directory and %1 is inputted folder name.
One option is to use QProcess to run external commands.
Example:
QString program = "move";
QStringList args;
args << QString("\"%1\\*.ico\"").arg(dir);
args << QString("\"%1\\%2\"").arg(dir).arg(images);
QProcess::execute(program, args);

execve(...) does not execute program despite passing in PATH variable

I'm executing a simple shell program from the directory:
/home/user/shell.exe
Using the code below, I'm able to run files that are in the same folder as my shell executable, but am unable to run programs such as ls.exe.
The tokens container includes the file name as the first element and any subsequent tokens (such as "-l" in the input "ls.exe -l") in the following elements.
if (fork())
{
int status;
wait(&status);
}
else
{
std::vector<const char*> exeArgs;
std::vector<const char*> envArgs;
std::for_each(tokens.begin(), tokens.end(),
[&exeArgs](const string& elem){ exeArgs.push_back(elem.c_str()); }
);
exeArgs.push_back(nullptr);
string path = "PATH=";
path.append(getenv("PATH"));
envArgs.push_back(path.c_str());
envArgs.push_back(nullptr);
if (execve(exeArgs[0], const_cast<char *const *>(&exeArgs[0]),
const_cast<char *const *>(&envArgs[0])))
{
std::cout << word << ": command not found" << std::endl;
exit(0);
}
}
I've spent countless hours just googling and reading the man pages over and over but can't seem to get a clue why this code doesn't work.
The idea is that my shell program should allow users to set the PATH variable and then execute programs with that PATH variable, which is why I have to make execve() work properly instead of just using execvp().
I have a map of shell variables in a separate part of the file but since I can't even get this to work, I thought it would be pointless to include that.
You do know that the exec family of functions replaces the current process with the image of the new program? That's why it's so common to use fork before exec.
Armed with that knowledge, it's easy to find a solution for you, and how you can use execvp (which you need to use, execve doesn't really use the environment you pass, it just passes it along to the new program): You fork and use setenv to set the PATH of the new process, before calling execvp.

Running a Bash Script with arguments as a Qt resource

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);

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)

QFileDialog "destroys" the name of the files

Right now I'm working on a GUI for a suite of console applications used for bioinformatics and I'm doing my first tests as its my first project using Qt. I'm using QtDesigner for making the GUI and everything works perfectly except that QFileDialog converts the end of the file name into a strange character, although I'm not really sure if it is QFileDialog, or the conversion from QString into const char.
Here is my code:
QString file=QFileDialog::getOpenFileName(this, tr("Open File"),"/home/",tr("Any file (*.*)"));
QString fastqdumpDir = "/home/nsg/Downloads/sratoolkit.2.1.16-centos_linux32/bin/"
fastqdumpDir=fastqdumpDir+"./fastq-dump ";
QString cmdStr =fastqdumpDir + file;
const char* command = cmdStr.toStdString().c_str();
system(command);
The fastq-dump program ends because it says that the filename is not correct, and after debugging, I see that the file name goes from /home/nsg/Downloads/SRR502947.sra into /home/nsg/Downloads/SRR502947.sra[] and sometimes even /home/nsg/Downloads/SRR5029[]
Any ideas why is this happening or how to fix it?
Your problem is that you are calling QString::toStdString(), which returns a temporary object, and trying to get a pointer it's contents. The memory pointed to becomes invalid as the std::string is destroyed. You don't need to the intermediate std::string at all. This should work:
QString cmdStr =fastqdumpDir + file;
system( qPrintable(cmdStr) );
Rather than using the system command to run the external program, you can use Qt's QProcess: -
QString cmdStr = fastqdumpDir + file;
QProcess::execute(cmdStr);
or
QProcess::startDetached(cmdStr)
if you don't want to wait for the process to finish. Since the functions take a QString, there's no need for the conversion to a const char*