2

PTY ライブラリを使用して Ruby でターミナル エミュレータを作成しています。/dev/tty0キーボードに接続されたデバイスファイルです。私はこのようにシェルを生成しています:

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

ほとんどの場合は機能しますが、サブプロセスがシェルで開始されると、shell[0]そのサブプロセスにキーボード入力が出力されません。例: を"cat\nasdf"介して送信するshell[1]と、を介し"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 を送信してプロセスを終了する必要があります。

4

1 に答える 1

2

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が2回表示されます。しかし、straceコードを見ると、catサブプロセスがasdfを1回だけ書き込み、その親プロセス、つまりシェルがasdfを書き込むことはないことがわかります。

では、なぜ2つの「asdf」出力があるのでしょうか。ttyレイヤーがローカルエコーを実行しているためです。そのため、シェルに何かを入力すると、それはptyとptyドライバーに送られます。

  • 疑似ttyのスレーブ側に書き込みます
  • マスター側にエコーします。

では、そうするとどうなりますsh -i </dev/tty0か?キーボードからの文字は、シェルの出力ではなく、/ dev/tty0にエコーされます。

シェルはエコーを実行していません。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をrawモードで構成し、selectを使用して、ブロックせずに読み取ることができる場合と、出力に使用できるデータがある場合を知る必要があります。

ttyをrawモードで構成するには、次を使用してみてください。

stty -F /dev/tty0 -cooked
于 2010-09-27T07:21:04.700 に答える