0

I have 2 bash scripts

  1. One script not requiring user input, it displays info
  2. The other requires some user input, and displays some info too

Problem:

When creating a QProcess, I am unable to differentiate if the QProcess is finished or is hanging for user input.

I was hoping for some function e.g. QProcess::atEnd that returns true if the script is finished, but it returns true even if the QProcess is hanging for user input (from script)

More Info:

  • Script requiring user input : name-please.sh

  • Script not requiring input : hello.sh

name-please.sh

#!/bin/bash
echo "I am Mr Robot"
echo "what is your name:"

read n

if [[ ! -z "$n" ]];
then
        echo "please to meet you"
        exit 0;
else
        echo "ok, bye..."
        exit 1;
fi

hello.sh

#!/bin/bash
echo "I am Mr Robot"

using mReadyReadStandardOutput(), I read the script output. The issue exists around the fact that I need to read the output, determine if the output contains a line that requests user info (line from the script) and respond to that.

see code with pointer to line

Code:

 #include <QCoreApplication>
#include <QLoggingCategory>
#include <QTextStream>
#include <QProcess>
#include <QString>
#include <QVariant>
#include <QDebug>
#include <QObject>
#include <QEventLoop>

class m_Proc : public QProcess
{
    Q_OBJECT

public:
    int exitCode = -1;
    QString _out, _input;

    m_Proc(QString input = QString(), QObject *parent = 0);
    ~m_Proc() {}

public slots:
    void myReadyReadStandardOutput();
    void myFinished(int i);
};

m_Proc::m_Proc(QString input, QObject *parent)
{
    connect(this,SIGNAL(readyReadStandardOutput()),
            this,SLOT(myReadyReadStandardOutput()));
    connect(this, SIGNAL(finished(int)),
            this, SLOT(myFinished(int)));
}

void m_Proc::myReadyReadStandardOutput() {
    // Note we need to add \n (it's like pressing enter key)
    _out = this->readAllStandardOutput();
    if (!_input.isEmpty()) {

        /* 
         * check if line matches that of the script, 
         * where the user is required to enter their name
         */

        if (_out.contains("what is your name")) {         <<< ---- LINE WITH ISSUE
            this->write(QString(_input + QString("\n")).toLatin1());
            _out = this->readAllStandardOutput();
        }
    }
    else
        qDebug() << "Input Emptry (should not be reached!) !";
}

void m_Proc::myFinished(int i){
    exitCode = i;
    qDebug() << "exit code = " << QString::number(exitCode);
    qDebug() << _out;
}

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

    m_Proc *myProcess1 = new m_Proc(QString("text"));

    //    QString program = "/home/dev/script";

    myProcess1->start("/home/dev/script", QStringList());// << "one" << "two");

    //    myProcess1->start("/bin/sh", QStringList() << "-c" << "ls" << "-la");


    a.exec();
}

#include "main.moc"

Any advice to bypass this issue?

Rama
  • 3,222
  • 2
  • 11
  • 26
CybeX
  • 2,060
  • 3
  • 48
  • 115
  • Or, instead of trying to reliably detect process quit you can try to actually quit the process while closing the Qt app. I am just unsure whether or not this can be solved by Qt detecting whether or not the process is still running. – Alexander V Jan 25 '17 at 23:57

1 Answers1

1

Three things.

  1. According to the documentation for QProcess::atEnd(), this returns true only if the process is not running (QProcess::NotRunning). So...are you sure the process isn't truly done when you call atEnd()? I don't see it anywhere in the code you've posted, so I can't tell.

  2. Your myReadyReadStandardOutput() signal is overwriting the m_Proc::_out member variable each time new data is available. Depending on how writes to standard output are buffered on your system (and by Qt), you may be reading partial lines into _out. Thus the _out.contains() call may return false, because you've got something like at is your in the line, rather than the full what is your name:.

  3. You write the member string _input to the process's standard input, and then immediately try to read again from the standard output. This will most likely not return any data that you didn't already have in _out. Qt only really does actual I/O with the underlying device when control returns to the main event loop. From the documentation for QIODevice:

Certain subclasses of QIODevice, such as QTcpSocket and QProcess, are asynchronous. This means that I/O functions such as write() or read() always return immediately, while communication with the device itself may happen when control goes back to the event loop.

My suggestion for solving your issue would be to modify the myReadyReadStandardOutput signal. For asynchronous devices (such as sockets and sub-processes), data may arrive in arbitrary-sized chunks. This is point 2 I made above. The usual method for dealing with this is to put something like the following at the top of the signal:

if (!canReadLine())
    return;

So you bail out of the signal and return to the main event loop if a full line of data is not available. If that returns true, you can do something like:

_out = readLine();
/* Process an entire line, e.g., check if it matches the request for name */

So the signal is designed to operate only on full lines of data. This is useful for terminals or network protocols that are line-oriented. In this case, you can call the QString::contains() method to check if this is the line requesting a name, and, if so, write it. But you must then return to the event loop in order for the data to be written, so you'd process the next line (please to meet you) in the next call to the signal in which a full line is available.

bnaecker
  • 6,152
  • 1
  • 20
  • 33
  • hi. thanks for your suggestions. I will implement your proposed solution. The point I was *trying* to make around the `::atEnd()` (being well aware of the function of `::atEnd()` as stated in the docs), is to use it as a hypothetical example of a function I would have like to use in order to check : is this the "final" output of the script or not. Possibly a function name of `::finalOutput()` - which returns true if the script is "finished" or the Process will close after this loop is complete. However it seems pointless now, either way I will need to process this output. Thank you! – CybeX Jan 26 '17 at 05:34
  • The main issue I was describing was also linked to a small sub issue of automating the `QProcess`. If you have a look at the `_out.contains("what is your name")`, this can be generalized to `_out.contains(someString)` where someString is a variable containing the next string to search for in the output. What happens if my input/response is determined by the output of the application. From this so far, I am thinking of creating a "application interface library" where this output string is passed, processed and returned to the current process. Does this seem like the correct method to use? – CybeX Jan 26 '17 at 05:44
  • @KGCybeX So the class itself will implement the logic of determining how to respond to the output of the application? As long as there's a well-defined relationship between the output and the response, that should be fine. I just mean that you have clear rules for creating `someString` as a function of the application's output. – bnaecker Jan 27 '17 at 05:14