Подтвердить что ты не робот

Ruby на Linux PTY уходит без EOF, поднимает Errno:: EIO

Я пишу код, который берет файл, передает этот файл в один из нескольких исполняемых файлов для обработки и отслеживает процесс преобразования для ошибок. Я написал и протестировал следующую процедуру на OSX, но linux не удалось по причинам, о которых я не знаю.

#run the command, capture the output so it doesn't display
PTY.spawn(command) {|r,w,pid|
    until r.eof? do
      ##mark
      puts r.readline
    end
}

Выполняемая команда сильно варьируется, и код в значке ## был упрощен в локальное эхо в попытке отладить проблему. Выполняется команда, а script печатает ожидаемый вывод в терминале и затем выдает исключение.

Ошибка, которую он производит в системах Debian, составляет: Errno::EIO (Input/output error - /dev/pts/0):

Во всех командных строках я могу придумать эту ошибку, и когда я запускаю код без локального блока эха, он работает просто отлично:

PTY.spawn(command) {|r,w,pid|}

В любом случае сама команда выполняется отлично, но похоже, что debian linux не отправляет eof pty. Страницы документа для PTY и IO на ruby-doc, похоже, здесь не помогают.

Любые предложения? Спасибо.

-vox -

4b9b3361

Ответ 1

Таким образом, мне нужно было дойти до чтения источника C для библиотеки PTY, чтобы получить удовлетворение от того, что происходит здесь.

Документ Ruby PTY на самом деле не говорит, что говорят в исходном коде.

Мое решение состояло в том, чтобы собрать метод оболочки и вызвать его из моего script там, где это необходимо. Я также включил метод ожидания, ожидающий завершения процесса, и доступ к статусу выхода из $?:

# file: lib/safe_pty.rb

require 'pty'
module SafePty
  def self.spawn command, &block

    PTY.spawn(command) do |r,w,p|
      begin
        yield r,w,p
      rescue Errno::EIO
      ensure
        Process.wait p
      end
    end

    $?.exitstatus
  end
end

Это используется в основном так же, как PTY.spawn:

require 'safe_pty'
exit_status = SafePty.spawn(command) do |r,w,pid|
  until r.eof? do
    logger.debug r.readline
  end
end

#test exit_status for zeroness

Я был немного расстроен, чтобы узнать, что это действительный ответ, поскольку он был полностью недокументирован в ruby-doc.

Ответ 2

Кажется, что для Errno:: EIO здесь может быть поднят (это просто означает, что дочерний процесс завершил и закрыл поток), поэтому вы должны ожидать этого и поймать его.

Например, см. выбранный ответ в Непрерывно читайте STDOUT внешнего процесса в Ruby и http://www.shanison.com/2010/09/11/ptychildexited-exception-and-ptys-exit-status/

Кстати, я провел некоторое тестирование. На Ruby 1.8.7 на Ubuntu 10.04 я не получаю сообщение об ошибке. С Ruby 1.9.3, я делаю. С JRuby 1.6.4 на Ubuntu в режимах 1.8 и 1.9 я не получаю сообщение об ошибке. В OS X, с 1.8.7, 1.9.2 и 1.9.3, я не получаю сообщение об ошибке. Поведение, очевидно, зависит от вашей версии Ruby и платформы.

Ответ 3

ruby-doc.org говорит это начиная с ruby 1.9:

# The result of read operation when pty slave is closed is platform
# dependent.
ret = begin
        m.gets          # FreeBSD returns nil.
      rescue Errno::EIO # GNU/Linux raises EIO.
        nil
      end

Итак, теперь я понимаю, что это поведение "нормально" в Linux, но это означает, что немного сложно получить вывод PTY. Если вы делаете m.read он читает все, а затем выбрасывает и вызывает Errno :: EIO. Вам действительно нужно читать фрагмент содержимого по фрагменту с помощью m.readline. И даже в этом случае вы рискуете потерять последнюю строку, если по какой-либо причине она не заканчивается на \n. Чтобы быть в большей безопасности, вам нужно прочитать содержимое побайтно с помощью m.read(1)

Дополнительное примечание о влиянии tty и pty на буферизацию: это не то же самое, что STDOUT.sync = true (небуферизованный вывод) в дочернем процессе, а скорее запускает буферизацию строки, где вывод сбрасывается на "\n"