C++ 在pty下运行命令时出现双回音

C++ 在pty下运行命令时出现双回音,c++,ssh,tty,pty,C++,Ssh,Tty,Pty,我正在编写一个程序来创建一个pty,然后fork并执行一个ssh命令,pty的从端作为它的stdin。完整的源代码在这里 using namespace std; #include <iostream> #include <unistd.h> #include <fcntl.h> int main() { int fd = posix_openpt(O_RDWR); grantpt(fd); unlockpt(fd); p

我正在编写一个程序来创建一个pty,然后fork并执行一个
ssh
命令,pty的从端作为它的
stdin
。完整的源代码在这里

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

        }

    }

}
我认为这是因为pty的行为与普通终端几乎相同。如果我添加
freopen(“log.txt”,“w”,stdout);“
并输入相同的命令,我只得到

echo hello #This is printed because I typed it.
log.txt
的内容如下:

~ $ echo hello #I think this is printed because a pty simulates input.
hello                             

~ $
如何避免重复


这是可以实现的吗?

我知道它在某种程度上是可以实现的,但不知道如何实现。事实上,
rlwrap
命令的行为与我的程序相同,只是它没有任何重复:

~/somedir $ rlwrap ssh user@192.168.11.40
~ $ echo hello
hello

~ $
我现在正在阅读
rlwrap
的源代码,但还没有理解它的实现


补充

正如(对我来说,不是答案,但OP是有帮助的。)中所建议的,取消设置
ECHO
终端标志将禁用双重回显。在我的情况下,将此片段添加到从属块解决了问题

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);
应该注意的是,这不是
rlwrap
所做的。就我所测试的
rlwrap
而言,任何
都不会重复其输入行。但是,我的程序会对某些
重复两次。例如

~ $ 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]:=    

这是因为
ssh
当我远程运行
wolfram
时)重新启用了回显功能吗?无论如何,我应该在孩子调用
exec()后,继续阅读
rlwrap
的源代码,正如您已经观察到的那样
从机端的终端标志不再受您的控制,子机可能(通常会)重新启用echo。这意味着在调用
exec
之前更改子机中的终端标志没有多大用处

并以各自(不同)的方式解决问题:

  • rlfe
    保留输入的行,但在显示之前保留子行的输出
  • rlwrap
    并让它被echo替换
无论您使用何种方法,您都必须知道您的输入是否已(在
rlfe
s情况下)或将(在
rlwrap
s情况下)被回显。
rlwrap
至少通过在父进程中不关闭pty的从属端,然后观察其终端设置来实现这一点(在这种情况下,
ECHO
位在其
c_lflag
中),以了解从机是否会回显


当然,所有这些都相当麻烦。
rlfe
方法可能更简单,因为它不需要使用
readline
库,您只需使用刚刚发送的输入
strcmp()(这只会在
cat
命令禁用其输入回声的情况下出错)

是否创建了伪终端,或者只是使用其终端接口驱动管道?(
rlwrap
作者:)正如您已经观察到的,在孩子调用了
exec
之后,从属端的终端标志不再受您的控制,孩子可能(并且经常会)重新启用echo。这两个
rlwrap
rlfe
都可以自己解决问题(不同)方法:要么在显示孩子的输出之前删除自己的输入,要么不显示此输出的第一行。我投票重新打开问题,以便给出正确的答案answer@HansLub哇。我真的很高兴收到原作者的评论。在我发布之前,我试图在显示孩子的输出之前删除你自己的输入这个问题,但没有成功,可能是因为。顺便说一句,最后我发现
wolfram
-rawterm
选项,可以避免命令重新启用回显。@HansLub我尝试编写类似于
rlwrap
的程序的原因是
rlwrap
不能与
ssh-t/usr/bin/wolfr>一起工作am
。现在我实现了
rl
(),它的功能实际上不如
rlwrap
,但可以与命令配合使用。
rlwrap
可以与
ssh/usr/bin/wolfram
配合使用(这里不是与
-t
)但是,在我的例子中,
-t
需要通过
ssh
(c.f.)向
/usr/bin/wolfram
发送一个信号。
wolfram
(不带
-rawterm
)在其提示符中包含控制字符,这会混淆
rlwrap
。使用
-rawterm
来防止这种情况,并使用
-a
(或
--始终读取行
rlwrap
选项(为了使
rlwrap
使用
readline
,即使
wolfram
将其终端置于原始模式,也会使
rlwrap
可用。对我来说,
rlwrap-a-pRed ssh-t localhost/usr/bin/wolfram-rawterm
乍一看似乎工作得很好。也就是说,
rlwrap
是为有你的简单控制台程序设计的。)t行编辑和历史记录。
wolfram
很难符合这一描述。我会记得这个很棒的技术实现:rlwrap至少是通过在父进程中不关闭pty的从属端,然后观察其终端设置来实现的
~ $ 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]:=