2010-09-27 43 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

相關問題