Question

I'm using Qt and bash over it, need to execute something like:

bash: cat file | grep string

in Qt:

QString cmd = "cat file | grep string";
QProcess *process = new QProcess;
process->start(cmd);
process->waitForBytesWritten();
process->waitForFinished();
qDebug() << process->readAll();

The problem is in pipe ("|"), and process returs nothing. If there is no ("|"), like

"cat file" 

everything is ok. I tried smth. like

"cat file \\| grep string", 
"cat file \| grep string" 

but result is the same. If I copy the command and run it in bash everything is ok.

QString::toAscii().data()

and other transforms also have bad result.

Was it helpful?

Solution

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

OTHER TIPS

The quick and dirty hack would be this:

QString cmd = "/bin/sh -c \"cat file | grep string\"";

You could also avoid the escaping in there with C++11's R"", but the point is that do not use bash in there because that will make it only work with bash. It will not work on embedded with busybox without bash, just ash, or any other common desktop shell.

/bin/sh is usually a symlink to the shell interpreter used, so that will eventually work.

BUT!

I think you are thinking a bit too low-level when using a high-level C++/OOP framework such as Qt. I would not recommend to invoke the commands in the low-level way when you run it from bash. There is some dedicated high-level convenience API for this use case.

Based on the official documentation, QProcess is supposed to work for pipe'd commands:

void QProcess::setStandardOutputProcess(QProcess * destination)

Pipes the standard output stream of this process to the destination process' standard input.

In other words, the command1 | command2 shell command command can be achieved in the following way:

QProcess process1;
QProcess process2;

process1.setStandardOutputProcess(&process2);

process1.start("cat file");
process2.start("grep string");
process2.setProcessChannelMode(QProcess::ForwardedChannels);

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

bool retval = false;
QByteArray buffer;
while ((retval = process2.waitForFinished()));
    buffer.append(process2.readAll());

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

qDebug() << "Buffer data" << buffer;

This is not the main point, but a useful suggestion: do not use QString::toAscii(). That API has been deprecated in Qt 5.

The problem is that when you call process->start(cmd), the commands following the the call to cat are all interpreted as arguments to cat, so the pipe is not doing what you're expecting. If you start with a call to bash with a parameter of a string, you should get what you want: -

QString cmd = "bash -c \"cat file | grep string\"";

Alternatively, you could just call "cat file" and do the search on the returned QString when you read the output from the QProcess

how about this :

QString program = "program";
QStringList arguments;

download = new QProcess(this);
download->start(program, arguments);

If Google brought you here and you are using PyQt5 or PySide2

        process1 = QProcess()
        process2 = QProcess()
        process1.setStandardOutputProcess(process2)
        process1.start(cat, [file])
        process2.start(grep, [string])   
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top