Ruby pipes: Как связать выходные данные двух подпроцессов вместе?
Существует ли автоматизированный способ создания оболочки оболочки в Ruby? Я пытаюсь преобразовать следующий код оболочки в Ruby:
a | b | c... > ...
но единственным решением, которое я нашел до сих пор, является сам управление буфером (упрощенный, непроверенный, надеюсь, что он получит мой смысл):
a = IO.popen('a')
b = IO.popen('b', 'w+')
Thread.new(a, b) { |in, out|
out.write(in.readpartial(4096)) until in.eof?
out.close_write
}
# deal with b.read...
Я предполагаю, что я ищу способ использовать popen для использования существующего потока, а не для создания нового? Или, альтернативно, метод слияния IO # для подключения вывода к входу b? Мой текущий подход становится довольно неудобным, когда число фильтров растет.
Я знаю о Kernel#system('a | b')
, очевидно, но мне нужно смешивать фильтры Ruby с внешними программными фильтрами в общем виде.
Ответы
Ответ 1
Старый вопрос, но поскольку его один из первых результатов в Google, вот ответ: http://devver.wordpress.com/2009/10/12/ruby-subprocesses-part_3/ (метод 8)
Короче:
sh = Shell.new
sh.system("a") | sh.system("b") | sh.system("c")
И вы можете делать более сложные вещи, такие как
sh.echo(my_string) | sh.system("wc") > "file_path"
xml = (sh.echo(html) | sh.system("tidy", "-q")).to_s
Ответ 2
Если a
, b
и c
- это команды, которые обычно доступны из командной строки, вы можете использовать:
captured_output = `a | b | c`
Ruby будет запускать команды в подчиненной оболочке и записывать STDOUT.
Если вам по какой-то причине нужно перенаправить вывод в файл, вы также можете добавить перенаправление к команде. STDOUT не будет возвращен вам в этом случае, но вы можете открыть файл и вручную обработать его:
`a | b | c > captured_output`
File.foreach('captured_output') do |li|
print li
end
Он не предлагает такого управления, как использование system
или popen3
, но это удобно:
>> sin, sout, serr = Open3.popen3('ls -al | tail -1') #=> [#<IO:fd 4>, #<IO:fd 5>, #<IO:fd 7>, #<Thread:0x00000100bb8798 run>]
>> sout.read #=> "drwxr-xr-x 3 greg staff 102 Nov 2 21:01 python\n"
Ответ 3
Используя простой рубин, spawn
имеет параметры перенаправления, которые вы можете использовать для подключения процессов к трубам.
1) Создайте трубку
r,w = IO.pipe
2) Используйте его для соединения двух нерешенных процессов
spawn(*%w[echo hello world], out: w)
spawn(*%w[tr a-z A-Z], in: r)
# => HELLO WORLD
Конечно, вы можете инкапсулировать это в нечто вроде sh.system
из указанной оболочки оболочки и создать метод |() для выполнения соединения.
В модуле open3 стандартной библиотеки есть действительно хорошие инструменты для такого рода материалов, включая создание полных конвейеров.
Ответ 4
К сожалению, трубопровод в оболочке - это серьезный вопрос, и, действительно, для этого потребуется довольно много кода. Не нужно создавать потоки с помощью циклов чтения/записи, но для этого все равно потребуется много работы.
Самый простой пример, который я нашел, - перенаправить реализацию в тире (Debian Almquist Shell). Как правило, если вы хотите сделать то же самое в Ruby, вам нужно будет реплицировать эти трюки манипуляции fd с помощью Ruby IO#dup
, IO#fileno
, IO#pipe
, IO#reopen
и т.д. Возможно, было бы проще повторно использовать shell (например, тире) в библиотеке C.so для интерпретатора Ruby, чем пытаться совместить одну и ту же вещь, используя примитивы Ruby.
Я не знаю какого-либо существующего обобщенного Ruby API для сложных межпроцессных трубопроводов/перенаправления. Если вы могли бы предложить хороший API, который вы хотели бы использовать, возможно, я мог бы участвовать в реализации.