Piping (Or Command Chaining) with Qprocess

Piping (or command chaining) with QProcess

The problem is you cannot run a system command with QProcess, but only a single process. So the workaround will be to pass your command as an argument to bash:

process.start("bash", QStringList() << "-c" << "cat file | grep string");

qprocess get shell like behaviour

Is there any Qt like way to handle that and force Qt to use some default command line interpreter?

The simple answer is no, there isn't a Qt default command line interpreter

QString command = "grep \"false negatives\" test.txt | cut -f2";

This command doesn't work because QProcess takes the first token (grep) and uses that as the command, then passes each item, separated by a space to that command. In this case, the pipe command is not a valid argument for grep and neither is cut, nor -f2.

I commented that the answer to this question was possibly similar, as it demonstrates how you can successfully use the pipe command with QProcess; note that the arguments are surrounded by quotes.

As you don't want to call cmd or a *nix equivalent such as bash, you can handle this with two calls to QProcess; the first for the grep command and the 2nd for the cut, passing in the output from the first QProcess call.

The function QProcess::setStandardOutputProcess makes this easier, allowing you to create the pipe directly between the two QProcess objects.

Therefore you'd do something like this: -

QProcess proc1;
QProcess proc2;

proc1.setStandardOutputProcess(&process2);

QString cmd1("grep \"false negatives\" test.txt");
QString cmd2("cut -f2");

proc1.start(cmd1);
proc2.start(cmd2);

Ampersand(&) (very command execute) with QProcess

First, let's figure out what happens when you execute a single command, like pwd. When you do it, the shell process forks (i.e. produces a child process), then the child process turns into pwd process and acquires control over the terminal.

Next, if you execute pwd &, all the same happens, but the child process (pwd in our example) does not get control over the terminal. It runs as a background process, while the shell continues to work with the terminal. For example, you may run a GUI program (firefox &), and the shell will be ready to run the next one immediately.

Finally, when you execute pwd & ls, all the same as in the previous case happens, but the shell forks one more time and runs ls in foreground. As you might guess, pwd & ls & yields both processes being run in background, and you may execute as many commands as you want at once.

Now let's get back to Qt. QProcess does not run any shell before running the command. Thus, when you run pwd & ls via QProcess, pwd will be the only program executed, but it will get two command line arguments: & and ls. Both of them will be ignored.

So the equivalent of pwd & ls will be two QProcess objects, each running a single command.

Another solution is to run the shell explicitly to make the arguments being parsed:

QString cmd = "tcsh -c \"pwd & ls\"";
QProcess *process = new QProcess;
process->start(cmd);
process->waitForBytesWritten();
process->waitForFinished();
qDebug() << process->readAll();

How to execute complex linux commands in Qt?

The key methods that exist for this purpose established in QProcess:

void QProcess::setProcessChannelMode(ProcessChannelMode mode)

and

void QProcess::setStandardOutputProcess(QProcess * destination)

Therefore, the following code snippet would be the equivalence of command1 | command2 without limiting yourself to one interpreter or another:

QProcess process1
QProcess process2;

process1.setStandardOutputProcess(&process2);

process1.start("echo myPass");
process2.start("sudo -S shutdown -r now");
process2.setProcessChannelMode(QProcess::ForwardedChannels);

// Wait for it to start
if(!process1.waitForStarted())
return 0;

bool retval = false;
QByteArray buffer;
// To be fair: you only need to wait here for a bit with shutdown,
// but I will still leave the rest here for a generic solution
while ((retval = process2.waitForFinished()));
buffer.append(process2.readAll());

if (!retval) {
qDebug() << "Process 2 error:" << process2.errorString();
return 1;
}

You could drop the sudo -S part because you could run this small program as root, as well as setting up the rights. You could even set setuid or setcap for the shutdown program.

What we usually do when building commercial Linux systems is to have a minimal application that can get setuid or setcap for the activity it is trying to do, and then we call that explicitly with system(3) or QProcess on Linux. Basically,

I would write that small application to avoid giving full root access to the whole application, so to restrict the access right against malicious use as follows:

sudo chmod u+s /path/to/my/application

QProcess: how to read output from pactl

The application you are trying to read from may be sending console output to stderr, in which case you have a few options:

  • just read from stderr outright: process.readAllStandardError()

  • set the read channel to read only from stderr: process.setReadChannel(QProcess::StandardError)

  • read from stderr and stdout with reckless abandon! : process.setProcessChannelMode(QProcess::MergedChannels)

Another possibility is that you are feeding incorrect arguments into your QProcess. It seems you are trying to pipe data above, the proper way to do this with QProcess is like this:

#include <QCoreApplication>
#include <QProcess>
#include <QDebug>

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);

QProcess pactl;
QProcess grep;
pactl.setStandardOutputProcess(&grep);
pactl.start("pactl list sinks short");
grep.start("grep 10_B7_F6_02_1B_4A");

pactl.waitForFinished();
grep.waitForFinished();

qDebug() << grep.readAll();
return EXIT_SUCCESS;
}


Related Topics



Leave a reply



Submit