代码之家  ›  专栏  ›  技术社区  ›  Adrian

当shell有子进程时,为什么ruby的PTY库不能捕获输入?

  •  2
  • Adrian  · 技术社区  · 14 年前

    /dev/tty0 是连接到键盘的设备文件。我像这样产卵:

    shell = PTY.spawn 'env TERM=ansi COLUMNS=63 LINES=21 sh -i < /dev/tty0'
    

    它基本上是工作的,但是当一个子进程在shell中启动时, shell[0] "cat\nasdf" 通过 shell[1] "cat" 从这里回来 外壳[0] 但是 "asdf" 没有。为什么会发生这种情况,我该如何解决?


    这是我的密码。 ChumbyScreen 是一个外部模块,用于控制我为之编写本文档的嵌入式设备的屏幕(称为“Chumby”)。这个 write 方法将字符放在屏幕上。

    require 'pty'
    
    def handle_escape(io)
      actions = 'ABCDEFGHJKSTfmnsulh'
      str, action = '', nil
      loop do
        c = io.read(1)
        if actions.include? c
          action = c
          break
        else
          str += c
        end
      end
      case action
      when 'J'
        ChumbyScreen.x = 0
      end
    end
    
    system '[ -e /dev/tty0 ] || mknod /dev/tty0 c 4 0'
    shell = PTY.spawn 'env TERM=ansi COLUMNS=63 LINES=21 sh -i < /dev/tty0'
    
    loop do
      c = shell[0].read(1)
      if c == "\e"
        c2 = shell[0].read(1)
        if c2 == '['
          handle_escape shell[0]
          next
        else
          c += c2
        end
      end
      ChumbyScreen.write c
    end
    

    require 'pty'
    
    def handle_escape(io)
      actions = 'ABCDEFGHJKSTfmnsulh'
      str, action = '', nil
      loop do
        c = io.read(1)
        if actions.include? c
          action = c
          break
        else
          str += c
        end
      end
      case action
      when 'J'
        ChumbyScreen.x = 0
      end
    end
    
    system '[ -e /dev/tty0 ] || mknod /dev/tty0 c 4 0'
    shell = PTY.spawn 'env TERM=ansi COLUMNS=63 LINES=21 sh -i'
    
    Thread.new do
      k = open '/dev/tty0', File::RDONLY
      loop do
        shell[1].write k.read(1)
      end
    end.priority = 1
    
    loop do
      c = shell[0].read(1)
      if c == "\e"
        c2 = shell[0].read(1)
        if c2 == '['
          handle_escape shell[0]
          next
        else
          c += c2
        end
      end
      ChumbyScreen.write c
    end
    

    1 回复  |  直到 14 年前
        1
  •  2
  •   shodanex    14 年前

    tty输入模式默认为行输入,因此在输出之前不会看到任何内容

    我建议使用 strace 调试这种行为。通过这种方式,您可以查看系统调用,例如,查看您是否在读取时被阻止,等待更多的输入等等。

    当您不使用“</dev/tty0”时,它确实有效,对吗? 基本上,你想要的是性格的共鸣。如果您执行以下操作:

    shell = PTY.spawn 'env TERM=ansi COLUMNS=63 LINES=21 sh -i'
    shell[1].puts("cat\nasdf")
    s = shell[0].read(16)
    puts s
    

    您可以使用以下方法来选择流程:

    strace -ff -o test.log -e trace=read,write ./testr.rb
    

    但是如果您查看strace代码,就会发现cat子进程只写一次asdf,而它的父进程(即shell)从不写asdf。

    那么,为什么会有两个“asdf”输出呢?因为tty层在做局部回波。因此,当您在shell中键入某个内容时,它将转到pty,而pty驱动程序:

    • 将其写入伪tty的从属端
    • 和主人的声音相呼应。

    sh -i </dev/tty0 ? 来自键盘的字符将回显到/dev/tty0,而不是shell的输出。

    # Emulate terminal behavior with pty
    shell = PTY.spawn 'env TERM=ansi COLUMNS=63 LINES=21 sh -i'
    keyboard = open(/dev/tty0)
    
    input = keyboard.read()
    shell[1].write(input)
    puts shell[0].read(...)
    

    现在,如果您想要一些交互式的东西,您将需要在raw模式下配置/dev/tty0,并使用select来知道何时可以不阻塞地读取,以及何时有数据可供输出。

    stty -F /dev/tty0 -cooked