Writing commands to linux bash



  • I have been looking for the answer to this super simple problem and have tried many different approaches. All i want to do is run a linux bash command from inside of my Qt program with QProcess.

    This is my code:

    QProcess *proc = new QProcess();
        proc->start("/bin/bash");
        proc->waitForStarted();
        qDebug() << proc->environment();
        proc->write("echo 'reboot' > /dev/ttyS0\n");
    
        qDebug() << proc->error() << ":" << proc->errorString();
    
    

    when i try to trigger this command, i do not get the intended result and my error string looks like

    QProcess::ProcessError(UnknownError) : "Unknown error"
    

    Very frustrating, I would appreciate some guidance.


  • Lifetime Qt Champion

    Hi,

    Because there was no error coming from bash.

    But since you want to write to a file, why not use QFile ? Or since you are using a serial port, why not QSerialPort ?



  • @SGaist QSerialPort sounds like a great idea, I am going to pursue that.

    But for the sake of learning (and I'm not sure how much you know about Linux & operating systems in general), if I wanted to do this the hard way, which more or less:

    //init a QProcess or something of the sort.
    //setProgram to serial console OR bash console.
    //if prog is serialconsole, write reboot command.
    //if prog is bashconsole, write echo reboot command.
    

    How would I go about doing this?

    I am going to do it the way you suggested by instantiating a QSerialPort, but I would love to learn how writing to bash or a serial console from Qt really works (as I am sure it more or less mirrors other frameworks and IDEs).

    Thank you!


  • Qt Champions 2017

    @devDawg

    Simple. Use QProcess, set /bin/bash as the command and the echo as parameter list. Note that parameter list is a QStringList and each parameter should be one QString in the list.

    By the way: It's you asking a question here, so you should not make assumptions about others knowledge. Nobody knows everything, but you got two very good suggestions which are the right solution for your problem.

    Regards



  • @aha_1980 Thank you!

    And if you look back at what I wrote...

    @devDawg said in Writing commands to linux bash:

    @SGaist QSerialPort sounds like a great idea, I am going to pursue that.

    But for the sake of learning (and I'm not sure how much you know about Linux & operating systems in general)

    I explicitly stated that I possessed no knowledge of @SGaist 's wisdom, meaning it would be downright silly of me to make assumptions about what he knows. I was simply trying to carry a constructive discussion.

    Thanks for the input, I appreciate it!

    Edit: Quickly breezed over QProcess and noticed that there is no method to set a command. Did you mean to say setProgram, or setProcessEnvironment? I know this may seem trivial, but I want to make sure I'm on the same page.



  • @SGaist @aha_1980

    Below is the code I am running:

    QSerialPortInfo *thing = new QSerialPortInfo();
    QList<QSerialPortInfo> ports = thing->availablePorts();
    
    
    QSerialPort *port = new QSerialPort(ports.at(0),this);
    port->setBaudRate(QSerialPort::Baud115200,QSerialPort::AllDirections);
    port->open(QSerialPort::ReadWrite);
    port->write("reboot");
    
    bool bytes = port->waitForBytesWritten(3000);
    qDebug() << "written: " << bytes;
    qDebug() << "flushed: " << port->flush();
    port->close();
    
    

    It actually does write successfully to the serial port, but doesn't actually execute the command. Even if I press enter after the text has popped into the console, it does nothing. So now that I've successfully written to the serial port console, how do I get this command to actually submit?

    Thanks


  • Qt Champions 2017

    @devDawg

    first of all: don't use the waitForReady.... methods, use signals&slots instead.

    second: which type of serial device are you talking to? do you need to add \r and/or \n?

    have you tried the Terminal example of QSerialPort?



  • @aha_1980 the device is a Grayhill 3D70 touch display.

    The issue is that whenever I press the "stop" button on my Qt Creator console, I have to manually reboot the device. I am trying to automate the reboot process upon app closure by sending the reboot command to the serial console myself whenever I stop my app.

    I just tried ending my reboot command with '\r' & '\r\n' respectively. I can visually see the command being sent to the serial console, but it is still not being submitted to the device; even if I press enter myself in the console to try and manually submit the command, it still doesn't do anything.

    I am going to try digging through their device manual for some information.

    EDIT: I have gone through the manual many times, and there doesn't appear to be anything that indicates how to remotely submit commands to the Linux serial console from an external program. Very frustrating.



  • @devDawg
    Pressing the "Stop Button" in QtCreator is not the correct way to close your application!

    Thats a forcefull termination and may result in unexprected behaviour. For example not all destructors are called. On unix systems for example QSharedMemorys are not correctly released and remain active even after "Closing" the App via that method.

    I would suggest a proper function that calls
    QApplication::quit in your program, wenn you want to close it.



  • @J.Hilk Duly noted, thank you. To add on to that , does pressing the red square emit a signal, so I could then connect that signal with QApplication::quit() ?

    The main issue that I'm trying to tackle right now, however, is unrelated to this. I have managed to write commands to my intended serial console, but they are not taking any effect, even when I end the command with \n or \r\n. Even if I write the command from pressing a button on my touchscreen GUI, then press Enter on my keyboard to submit the command, the serial console doesn't do anything (almost as if it hasn't detected any typing from the user, or something?)

    I would love to be able to solve this problem, please suggest any ideas you may have!

    EDIT: For configuring my serial communication, is it necessary to incorporate stop/start bits to notify the serial port that I am trying to submit a command?

    Current code that is not working:

    QSerialPortInfo *thing = new QSerialPortInfo();
    QList<QSerialPortInfo> ports = thing->availablePorts();
    
    
    QSerialPort *port = new QSerialPort(ports.at(0),this);
    port->setBaudRate(QSerialPort::Baud115200,QSerialPort::AllDirections);
    port->setDataBits(QSerialPort::Data8);
    port->setParity(QSerialPort::NoParity);
    port->setStopBits(QSerialPort::OneStop);
    port->setFlowControl(QSerialPort::NoFlowControl);
    port->open(QSerialPort::ReadWrite);
    port->setDataTerminalReady(true);
    port->write("reboot\r\n");
    //port->write("reboot\n");
    //port->write("reboot\r");
    //port->write("reboot");
    bool bytes = port->waitForBytesWritten(3000);
    qDebug() << "written: " << bytes;
    qDebug() << "flushed: " << port->flush();
    port->readAll();
    
    port->close();
    

    This code is inside of a slot that gets called when I press a quit button that I've instantiated on the touchscreen.

    When I press the button, I see the text getting written in the console; it just does nothing.

    Here is a picture of the serial console to give you more context:
    0_1530796326440_console.png

    PS: I know I shouldn't be using waitForBytesWritten, as it was previously pointed out.. I just didn't care to change that portion of code, as I did not think it was related to my current issue.



  • @devDawg
    Excuse my coming late to the party. I know nothing about "serial consoles" but (I think) I do know about terminals & shells.

    I don't get what's going on here, or what you're trying to achieve. From what I can see you are writing characters into some kind of terminal window, but you haven't got a shell (bash) which is attempting to take what you're typing/sending and treat it as an interactive command on stdin to execute. I don't think there's anything here that matters about signals/slots or CR/LFs.

    Would you care to explain what you're actually trying to achieve, because I don't get it? And initially at least you said nothing about serial ports and didn't have any serial port code, does your question/do I need to understand anything about this serial port stuff, because I might be able to help better if that's not involved?

    Initially you seemed to just ask:

    All i want to do is run a linux bash command from inside of my Qt program with QProcess.

    If all you wanted to do was execute reboot you could just do so without any bash or /dev/ttyS0? That would run reboot on the machine where your Qt app is running, which is all your question seems to ask for.

    Now I'm wondering if /dev/ttyS0 is some serial terminal which is already running a shell, which you want to ask to do a reboot?? I'm lost!



  • @JonB So from my primitive understanding, opening up a serial console provides access to the bash of the serially connected device.

    I've got a real-time data application that I am building for a Grayhill 3D70 touchscreen tablet. At this moment, I send commands to this device by opening a serial connection on PuTTY.

    What I'm trying to achieve: upon closing my application or pressing an on-screen 'quit' button, I would like to send the command 'reboot' to my device console to reboot, as this is a necessary operation to run an application after closing a previous one on this particular hardware.

    The reason I said nothing about serial ports initially is because I was trying to achieve this by echoing a command from the Linux bash within my virtual machine (echo 'reboot' > /dev/ttyS0), upon realizing that I was overcomplicating things.

    I'm sorry for being confusing. All i want is for my Linux device to 'reboot' upon app closure!

    If all you wanted to do was execute reboot you could just do so without any bash or /dev/ttyS0? That would run reboot on the machine where your Qt app is running, which is all your question seems to ask for.

    How would I go about doing this?? I would love to know!

    Now I'm wondering if /dev/ttyS0 is some serial terminal which is already running a shell, which you want to ask to do a reboot?? I'm lost!

    You are mostly correct on that. The device does not provide me access to its terminal, so I have to open up a serial console in a virtual machine to talk to its shell instead.



  • @devDawg
    OK, at least I understand better now! As you say, your question has nothing to do with the initial question about QProcess. And you don't want to run anything from within your Qt app, you want it to get the serial-port-bash-console to receive some characters as a command to execute.

    So from my primitive understanding, opening up a serial console provides access to the bash of the serially connected device.

    So the serial device is there already running a bash, sitting waiting for a new command to arrive? (E.g. You don't have to log in now, just send it a command?) You have tested this outside of any Qt app? And you don't happen to have non-Qt code which does what you are trying to achieve in Qt?



  • @JonB

    So the serial device is there already running a bash, sitting waiting for a new command to arrive? (E.g. You don't have to log in now, just send it a command?)

    I have a serial bash on my virtual machine that is connected to the device. Typing the reboot command manually does exactly what it's supposed to; I'm just tired of writing this command out every time. I would like it to auto-reboot upon closure.

    You have tested this outside of any Qt app?

    Yes.. The serial console accepts my reboot command when I type it in manually.

    And you don't happen to have non-Qt code which does what you are trying to achieve in Qt?

    While I have been looking, I have yet to find a really good example online. It is difficult to determine what exactly I should even be searching to get relevant results. Currently looking for similar issues.



  • @devDawg
    So forget Qt for now, can you provide a shell script (on your controlling Linux box) which does whatever to send the reboot to the serial port console and have it execute it?



  • @JonB

    rebootscript.sh:

    #!/bin/bash
    echo 'reboot' > /dev/ttyS0
    
    

    I tested this by entering the command ". rebootscript.sh" when inside of its directory. It worked as expected.



  • @devDawg
    Oh! Now I understand that bit!

    So we would indeed have thought that at least one of the attempts you show in your code, with the various ends-of-line, would indeed have achieved same....

    To be same as script, you could open port only for write instead of r/w, but I doubt that would make a difference? Meanwhile does your existing port->readAll() return some kind of message?

    To try to get it working just as a shell command, let's return to your very original attempt. Try:

    proc->start("/bin/bash", QStringList() << "-c" << "echo 'reboot' > /dev/ttyS0");
    

    (and no proc->write()).

    ?

    (If you're prepared to make chmod +x rebootscript.sh you can also then try just proc->start("./rebootscript.sh").)



  • @JonB Holy moly.. It worked. Why the extra "-c" argument? Is this to denote console?



  • @devDawg
    Meanwhile, returning the /dev/ttyS0 direct accessing.

    @SGaist wrote:

    Or since you are using a serial port, why not QSerialPort ?

    Now beware, because almost always he is right and I am wrong(!), but I think this took us down a garden path. We only want to emulate echo 'reboot' > /dev/ttyS0. Now when bash does this line (I'm pretty sure) it does not do anything about looking to see that this is a serial port device and opening it with any kind of serial port code like your code attempted. It just passes /dev/ttyS0 to the standard Linux open(), and the device handler takes care of the serial-port-ness. So I would have thought you'd just want to pass that to plain old QFile and open for write.



  • @devDawg said in Writing commands to linux bash:

    @JonB Holy moly.. It worked. Why the extra "-c" argument? Is this to denote console?

    That's why UNIX provides man bash :)

    The simplest way to get any of the shells to execute a simple command is:

    /bin/bash -c "the command as a single argument"
    

    The -c says that a single argument follows of the entire command to be executed. Nothing to do with console.

    Your original code did not pass that, and instead tried to run a bash without that argument, and present the command to be executed on the shell's stdin (same as -s argument). I am unsure why that did not in fact work, but using -c is preferable in any case.

    It's good that this works now. But still a bit of overkill just to get a string sent to a device. I do think you should retry the "file" approach, but just use QFile (make sure you close it at the end) on /dev/ttyS0 instead of all that serial port code.



  • @JonB You may think it is overkill, and you are probably right, but it accomplishes what I need. For the meantime, I will settle for this so I can focus on other tasks at hand.

    I really appreciate your patience and willingness to help, thank you @JonB !


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.