What I'm trying to achieve is to open a new terminal from a C/C++ program and run vim. I'm doing this by forking and execing "xterm -e vim [fname]". Try as I might, I can't seem to get xterm to understand what it is I want it to do.
Below is the relevant code segment:
int pid = fork();
if (pid){
//parent
int retstat;
waitpid (pid, &retstat, 0);
}else{
//child
char* ifname_cchararr = (char*)malloc(ifname.length() + 1);
strcpy (ifname_cchararr, ifname.c_str());
char* const argv[4] = {"-e", "vim", ifname_cchararr, NULL};
// std::cout << ifname_cchararr<<std::endl;
execvp ("xterm", argv);
}
Running the program results in xterm complaining:
-e : Explicit shell already was /usr/bin/vim
-e : bad command line option "testfile"
I get the feeling I've messed up argc somehow, but I'm confused, because running the following in an xterm window:
xterm -e vim testfile
works perfectly fine.
Please enlighten me!
You forgot to add xterm as first argument in argv. It may seems a bit weird, that you have to add the program-name to argv, since you already tell execvp which program you're calling, but thats how it is. For more information to why, see this recently asked question on Unix & Linux: Why does argv include the program name
Related
I'm writing a program to create a pty, then fork and execute an ssh command with the slave side of the pty as its stdin. The full source code is here.
using namespace std;
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = posix_openpt(O_RDWR);
grantpt(fd);
unlockpt(fd);
pid_t pid = fork();
if (pid == 0) { //slave
freopen(ptsname(fd), "r", stdin);
execlp("ssh", "ssh", "user#192.168.11.40", NULL);
} else { //master
FILE *f = fdopen(fd, "w");
string buf;
while (true) {
getline(cin, buf);
if (!cin) {
break;
}
fprintf(f, "%s\n", buf.c_str());
}
}
}
After executing this program and inputting just echo hello (and a newline), the child command re-sends my input before its own output, thus duplicating my input line:
~ $ echo hello
echo hello #duplication
hello
~ $
I think this is due to the fact that a pty behaves almost the same as a normal terminal. If I add freopen("log.txt", "w", stdout);" and input the same command, I get just
echo hello #This is printed because I typed it.
and the contents of log.txt is this:
~ $ echo hello #I think this is printed because a pty simulates input.
hello
~ $
How can I avoid the duplication?
Is that realizable?
I know it is somehow realizable, but don't know how to. In fact, the rlwrap command behaves the same as my program, except that it doesn't have any duplication:
~/somedir $ rlwrap ssh user#192.168.11.40
~ $ echo hello
hello
~ $
I'm reading the source code of rlwrap now, but haven't yet understood its implementation.
Supplement
As suggested in this question (To me, not the answer but the OP was helpful.), unsetting the ECHO terminal flag disables the double echoing. In my case, adding this snippets to the slave block solved the problem.
termios terminal_attribute;
int fd_slave = fileno(fopen(ptsname(fd_master), "r"));
tcgetattr(fd_slave, &terminal_attribute);
terminal_attribute.c_lflag &= ~ECHO;
tcsetattr(fd_slave, TCSANOW, &terminal_attribute);
It should be noted that this is not what rlwrap does. As far as I tested rlwrap <command> never duplicates its input line for any <command> However, my program echoes twice for some <command>s. For example,
~ $ echo hello
hello #no duplication
~ $ /usr/bin/wolfram
Mathematica 12.0.1 Kernel for Linux ARM (32-bit)
Copyright 1988-2019 Wolfram Research, Inc.
In[1]:= 3 + 4
3 + 4 #duplication (my program makes this while `rlwrap` doesn't)
Out[1]= 7
In[2]:=
Is this because the <command> (ssh when I run wolfram remotely) re-enables echoing? Anyway, I should keep reading the source code of rlwrap.
As you already observed, after the child has called exec() the terminal flags of the slave side are not under your control anymore, and the child may (and often will) re-enable echo. This means that is is not of much use to change the terminal flags in the child before calling exec.
Both rlwrap and rlfe solve the problem in their own (different) ways:
rlfe keeps the entered line, but removes the echo'ed input from the child's output before displaying it
rlwrap removes the entered line and lets it be replaced by the echo
Whatever approach you use, you have to know whether your input has been (in rlfes case) or will be (in rlwraps case) echoed back. rlwrap, at least, does this by not closing the pty's slave end in the parent process, and then watching its terminal settings (in this case, the ECHO bit in its c_lflag) to know whether the slave will echo or not.
All this is rather cumbersome, of course. The rlfe approach is probably easier, as it doesn't require the use of the readline library, and you could simply strcmp() the received output with the input you just sent (which will only go wrong in the improbable case of a cat command that disables echo on its input)
I want to write a routine that will automatically change my Machine learning parameters and execute the (Machine learning) code multiple times and save its result into a text file.
The Machine learning code is run from the Ubuntu terminal and its result also are written to the Ubuntu terminal.
I need a way to read data and write data to the Ubuntu terminal from the C++ code.
What I have found so far is the QProcess:
QProcess process;
process.start("gnome-terminal"); //THE TERMINAL IS OPENED INDEED
char w[] = "write";
process.write(w,sizeof(w));
process.waitForFinished();
But nothing is written or read from it
What does working is:
QProcess process1;
QProcess process2;
process1.setStandardOutputProcess(&process2);
process1.start("echo 0534230313");
process2.start("sudo -S poweroff");
process2.setProcessChannelMode(QProcess::ForwardedChannels);
And also this works:
int result = system("echo 0534230313 | sudo -S poweroff"); ///COMMANDS TO TERMINAL
int result2 = system("gnome-terminal"); ///OPEN TERMINAL
But the problem is that I wont be able to read the output result (My Machine learning program can open and write to the terminal by itself)
Use the finished signal to read the process output results
connect( poProcess, static_cast<void (QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished),
[=](int,QProcess::ExitStatus)
{
QByteArray oProcessOutput=
poProcess->readAllStandardOutput();
// Parse the process output.
//
// Mark process for deletion.
emit poProcess->deleteLater();
});
I have a path to a folder and a command to execute from that folder. It's an arbitrary complex command. For ex.: qmake -v > 1.txt.
I figure I need to run a shell (cmd.exe because I'm on Windows atm.), and execute the command there:
QProcess::startDetached("cmd.exe", QString("qmake -v > 1.txt").split(' ',
QString::SkipEmptyParts), "C:/folder/");
But it doesn't work. It does launch a console window (cmd.exe) at the specified path, but doesn't execute the command. Then I've tried:
QProcess::startDetached("cmd.exe", QStringList() << "start" << QString("qmake -v > 1.txt").split(' ', QString::SkipEmptyParts), "C:/folder/");
Also no luck. And finally, just to test if I'm going anywhere with this at all:
QProcess::startDetached("qmake.exe", QString("-r -tp vc").split(' ', QString::SkipEmptyParts), "C:/folder_containing_qt_project/");
A console window appears for a brief moment, but project is not generated.
What am I doing wrong, and how can I achieve what I want with QProcess (I don't mind using WinAPI if there's no other way, but strongly prefer to use the same code on Linux / Mac OS X / Windows)?
It might be worth noting that in another method of the same app I have no problem executing notepad.exe <path to a text file> with startDetached.
I think you are looking for this method:
void QProcess::setStandardOutputFile(const QString & fileName, OpenMode mode = Truncate)
You would be using it then like this:
QProcess myProcess;
myProcess.setStandardOutputFile("1.txt");
myProcess.start("qmake -v");
myProcess.waitForFinished();
You could also read the output into the application and write that out with QFile. This may get you going into that direction:
QByteArray QProcess::readAllStandardOutput()
Regardless of the current read channel, this function returns all data available from the standard output of the process as a QByteArray.
Therefore, you could write something like this, but it is a bit more complicated, and potentially memory-hungry, so it is just for demonstratation purposes without chunk read and write.
QProcess myProcess;
myProcess.start("qmake -v");
myProcess.waitForFinished();
QFile file("1.txt");
file.open(QIODevice::WriteOnly);
file.write(myProcess.readAllStandardOutput());
file.close();
Disclaimer: I intentionally did not write error checking to keep it simple.
I'm working in a project with a quadrotor and mavlink. I have successfully installed mavproxy in my Ubuntu PC and and ran it without problems from terminal. When I run mavproxy.py from the terminal and connected a quadrotor with support for mavlink (APM autopilot), mavproxy detects the quadrotor and everything is ok.
When you execute mavproxy.py the program in the terminal begin to send and receive several parameters. You can write in the terminal some parameter to access for any configuration. For example, the command help in the terminal:
$ mavlink.py
.
.data beging
.
STABILIZE> "when the program finish the configuration, allowed to you for doing an input any parameter, for example help"
STABILIZE>help
show all helps.
I have a code to execute mavlink.py from C++
include <iostream>
include <stdio.h>
using namespace std;
int main() {
FILE *in;
char buff[512];
if(!(in = popen("mavlink.py", "r"))){
return 1;
}
while(fgets(buff, sizeof(buff), in)!=NULL){
cout << buff;
}
pclose(in);
return 0;
}
When I run this C++ program the terminal shows the same things that would appear if I were running mavproxy.py from the terminal, but I donĀ“t know how can I send a command such as help in the C++ code.
If you read the program, the while statement allows me to capture the parameters generated from the program mavproxy.py and cout in the terminal, but mavlink.py never ends until you write something in the terminal exit or press CTRL + C so the while loop never ends.
I have been reading about the Popen function, but I haven't found the correct form to do this.
I know that I can use the mavlink.h library in my program and send parameters to the quadrotor, but don't want do this with mavlink.h.
I am not sure I understand your question, but I think you want to send commands to mavlink.py as well as read its output.
If that is the case, you must change the open mode of popen() from "r" to "w" so you can write, then you can send commands to it like this:
FILE *fp;
char *command="HELP";
if(!(fp = popen("mavlink.py", "w"))){
return 1;
fwrite(command, sizeof(char), strlen(command), fp);
argv = {'/usr/bin/grep', '/usr/bin/grep', '-ri', 'test', '.', 0}
pid_t pid = fork();
if (pid == 0) {
execv(argv[0], argv);
exit(0);
}
else {
wait(NULL);
}
This is what I have for my code. I am simply trying to get the grep command to run. The problem I run into is that it thinks the second /usr/bin/grep is the search pattern.
I have tried to change the argv array to only contain one /usr/bin/grep, but that errors out and does not run the grep. Any help?
execv takes the command as the first parameter so you want "usr/bin/grep" not argv[0] which is the program you are running. Also remove the first parameter from argv.
I would also check that grep is in /usr/bin it's often in /bin.