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

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", "[email protected]", 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());

        }

    }

}

执行此程序并仅输入echo hello(和换行符)后,child命令在其自身输出之前重新发送我的输入,从而复制了我的输入行:
~ $ echo hello
echo hello #duplication
hello

~ $

我认为这是由于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 [email protected]
~ $ echo hello
hello

~ $

我正在阅读rlwrap的源代码,但尚未理解其实现。

补充

this question中的建议(对我来说,不是答案,但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 <command>从不重复任何<command>的输入行。但是,我的程序为<command>重复了两次。例如,
~ $ 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]:=

这是因为<command>(当我远程运行ssh时为wolfram)重新启用了回显吗?无论如何,我应该继续阅读rlwrap的源代码。

最佳答案

正如您已经观察到的那样,在子进程调用exec()之后,从属端的终端标志不再受您的控制,并且该子进程可能(并且经常会)重新启用echo。这意味着在调用exec之前更改子级中的终端标志没有太大用处。

rlwraprlfe都以自己(不同)的方式解决了这个问题:

  • rlfe保留输入的行,但是在显示子项之前从 child 的输出中输出removes the echo'ed input
  • rlwrap removes the entered line 并将其替换为echo

  • 无论使用哪种方法,都必须知道您的输入是回显还是回显(对于rlfe而言)。至少,rlwrap可以通过不关闭父进程中pty的从属端,然后查看其终端设置(在本例中为rlwrap中的ECHO位)来知道从属是否回显,从而做到这一点。

    当然,所有这些都非常麻烦。 c_lflag方法可能更容易,因为它不需要使用rlfe库,并且您可以使用刚刚发送的输入来简单地对接收到的输出进行readline(这只会在strcmp()命令禁用的可能性很小的情况下出错)在其输入上回显)

    关于c++ - 在pty下运行命令时出现双重回声,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59007528/

    10-10 00:20