Как исправить зависание popen3 в Ruby?
Я получаю неожиданное поведение, используя popen3, который я хочу использовать для запуска команды, как инструмент ала cmd < file1 > file2
, Приведенный ниже пример зависает, так что stdout done
никогда не достигается Используя другие инструменты, чем cat
может привести к зависанию, так что stdin done
никогда не достигается Я подозреваю, что страдаю от буферизации, но как мне это исправить?
#!/usr/bin/env ruby
require 'open3'
Open3.popen3("cat") do |stdin, stdout, stderr, wait_thr|
stdin.puts "foobar"
puts "stdin done"
stdout.each_line { |line| puts line }
puts "stdout done"
puts wait_thr.value
end
puts "all done"
3 ответа
stdout.each_line
ждет дальнейшего вывода из cat
так как cat
выходной поток по-прежнему открыт. Это все еще открыто, потому что cat
все еще ожидает ввода от пользователя, потому что его поток ввода еще не был закрыт (вы заметите, что когда вы откроете cat
в терминале и введите foobar
, он все еще будет работать и ждать ввода, пока вы не нажмете ^d
закрыть поток).
Чтобы это исправить, просто позвоните stdin.close
прежде чем распечатать вывод.
Ваш код висит, потому что stdin
все еще открыт!
Вы должны закрыть его IO#close
или с IO#close_write
если вы используете popen3
,
Если вы используете popen
тогда вам нужно использовать IO#close_write
потому что он использует только один дескриптор файла.
#!/usr/bin/env ruby
require 'open3'
Open3.popen3("cat") do |stdin, stdout, stderr, wait_thr|
stdin.puts "foobar"
stdin.close # close stdin like this! or with stdin.close_write
stdout.each_line { |line| puts line }
puts wait_thr.value
end
Смотрите также:
Ответы Тило и Сеппка верны: если вы закроете stdin
твой простой тест закончится. Задача решена.
Хотя в своем комментарии к ответу sepp2k вы указываете, что у вас все еще возникают зависания. Ну, есть некоторые ловушки, которые вы могли не заметить.
Застрял на полный буфер для stderr
Если вы вызываете программу, которая печатает в stderr больше, чем может вместить буфер анонимного канала (64 КБ для современных Linux), программа приостанавливается. Приостановленная программа не выходит и не закрывает стандартный вывод. Следовательно, чтение из его стандартного вывода будет зависать. Так что, если вы хотите сделать это правильно, вы должны использовать темы или IO.select
, неблокирующее, небуферизованное чтение для чтения из stdout и stderr параллельно или по очереди без застревания.
Застрял на полный буфер для стандартного ввода
Если вы пытаетесь кормить больше (намного больше), чем "foobar" в вашей программе (cat
), буфер анонимного канала для stdout заполнится. ОС будет приостановлена cat
, Если вы напишете даже больше в stdin, буфер анонимного канала для stdin заполнится. Тогда ваш звонок stdin.write
застрянет. Это означает: вам нужно писать в stdin, читать из stdout и читать из stderr параллельно или по очереди.
Conlusion
Прочитайте хорошую книгу (Ричардс Стивенс, "Сетевое программирование в UNIX: межпроцессное взаимодействие") и используйте хорошие библиотечные функции. IPC (межпроцессное взаимодействие) слишком сложен и склонен к неопределенному поведению во время выполнения. Слишком много хлопот, чтобы попытаться сделать это правильно методом проб и ошибок.
использование Open3.capture3
,