2010-09-27 17 views
2

我正在使用PTY库在ruby中编写终端模拟器。 /dev/tty0是连接到键盘的设备文件。我产卵壳这样的:为什么ruby的PTY库在shell有子进程时无法捕获输入?

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

它主要的工作,但是当一个子进程在shell启动,shell[0]不输出输入到该子键盘。例如:当我通过shell[1]发送"cat\nasdf"时,"cat"通过shell[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 

阅读shodanex的回答后,我尝试这样做:

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 

它的工作原理,但我已经输入的字符显示不出来,直到我按enter键。它必须以某种方式进行线路缓冲 - 我如何克服这一点?另外Control-C和Control-D什么都不做。我需要他们发送一个eof并终​​止一个进程。

+0

你可以发表你的整个Ruby代码? – shodanex 2010-09-29 08:43:00

+0

@shodanex:编辑。您的建议迄今为止有帮助,谢谢! – Adrian 2010-09-30 03:34:13

+0

默认情况下,tty处于行读取模式。您可以尝试在原始模式下使用它,并在您的代码中处理Ctrl-C和Ctrl-D。 – shodanex 2010-09-30 09:59:09

回答

1

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使用过程:

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

在输出,你会看到ASDF两次。 但是,如果您查看strace代码,您会发现cat子进程只会写入asdf一次,而它的父进程(即shell)从不会写入asdf。

那么为什么会有两个'asdf'输出?因为tty层正在执行本地回显。所以,当你在锅壳式的东西它会发送到pty,和pty驱动:

  • 写伪TTY
  • 的从侧面呼应返回到主面。

那么当你做sh -i </dev/tty0时会发生什么?来自键盘的字符被回显到/ dev/tty0,而不是回到shell的输出。

外壳没有做任何回音,tty的层,所以你想要做的是如下因素(把它当作伪代码,我不能胜任红宝石):

# 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(...) 

现在,如果你想交互一些东西,你需要在原始模式下配置/ dev/tty0,并使用select来知道什么时候你可以读取而没有阻塞,什么时候有数据可用于输出。

配置在原始模式TTY,您可以尝试使用

stty -F /dev/tty0 -cooked 
+0

但是在工作终端模拟器中,当你运行'cat'并键入内容并按下回车键时,你可以看到你键入的东西两次,一次来自键盘输入,另一次来自'cat'。在我的,你只能看到'cat'的输出。 – Adrian 2010-09-28 00:26:34

+0

您的'stty'解决方案奏效,谢谢! – Adrian 2010-10-01 03:45:58

相关问题