ruby-talk の話見ててちょっと思いだしたんですが....

1.7.x のどこかから PTY.spawn は子プロセスの監視にスレッドを使うように
なりました.

IO#expect は IO.select を使ってタイムアウト付きの expect を実装してい
ます.

スレッドを使うと select() がスレッドのスケジューリングと干渉して,使え
る fd がなくても帰ってきてしまう場合があります.

# これは ruby-talk ですでに報告されていて,patch がとりこまれています
# が完全な fix はまだだったはず?


....総合すると,「何も考えずに pty の読みだしの方を expect にかけると 
IO.select が途中で返ってきて,getc が nil を返すから nil.chr が実行さ
れ NoMethodError で落ちます」.

さらに pty.spawn のブロックは「子スレッドの方で実行される」ので例外か
ら recover しようとしたらブロック内で rescue する必要があります.また
clearerr() を呼ぶために rescue close で.... 何がいるんでしたっけ?


で,実は expect.rb を require して IO.expect を使うより,IO#sysread を
使って EOFError を rescue しておいた方が簡単かもしれません.

たとえば今使ってるコードだと ssh を spawn してプロンプトが返ってくるの
を待つのに

    require "pty"

    $port = 12345       # local port

    begin
      PTY.spawn("ssh -L #{$port}:remote:110 remote") do
        |rf, wf|
    
        res = ""
        begin
          res << rf.sysread(128).to_s until res =~ /\$/
        rescue EOFError
          sleep 0.5
          retry
        end
        
        # some useful stuff.
      end
    rescue PTY::ChildExited
      # ignore.
    end

なんていうことをしています.

# 省略したところはまあ,想像の付くとおり :-) 実はもうちょっとましなや
# り方は無いものかとも思ってますが.


Just FYI.

# 実のところ見てる範囲でこんなんで引っかかってる人はいないんで,実はわ
# ざわざいうほどのものでもないという可能性もあり?


-- 
柳川和久 @ 東大阪市 . 大阪府                               January 14, 2002
「うむ,奥が深い」「....底は浅いけどね」