Ruby pipe: Как связать выходные данные двух подпроцессов? - PullRequest
16 голосов
/ 20 ноября 2010

Существует ли автоматизированный способ создания оболочки в 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 # для подключения выхода а к входу б?Мой текущий подход становится довольно громоздким, когда число фильтров растет.

Я знаю о Kernel#system('a | b'), очевидно, но мне нужно смешать фильтры Ruby с внешними программными фильтрами в общем виде.

Ответы [ 4 ]

10 голосов
/ 19 января 2011

Старый вопрос, но так как его один из первых результатов в 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
3 голосов
/ 21 ноября 2010

Если 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"
1 голос
/ 10 марта 2015

Используя обычный ruby, 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 из упомянутой библиотеки Shell и создать метод | () для выполнения взаимосвязи.

open3В модуле стандартной библиотеки есть несколько действительно хороших инструментов для такого рода вещей, включая создание законченных конвейеров.

0 голосов
/ 27 ноября 2010

К сожалению, передача в оболочку - это серьезный вопрос, и, действительно, для этого потребуется немало кода.Нет необходимости создавать потоки с циклами чтения / записи, но это все равно потребует большой работы.

Простейший пример, который я нашел, - реализация перенаправления в dash (Оболочка Debian Almquist) .Как правило, если вы хотите сделать то же самое в Ruby, вам нужно будет повторить эти fd приемы манипуляции с использованием Ruby's IO#dup, IO#fileno, IO#pipe, IO#reopen и т. Д.Возможно, было бы проще повторно использовать код оболочки (такой как dash) в библиотеке C .so для интерпретатора Ruby, чем пытаться объединить одно и то же, используя только примитивы Ruby.

Я не знаю ни одного существующего обобщенного API Rubyдля сложных межпроцессных трубопроводов / перенаправления.Если бы вы могли предложить хороший API, который вы хотели бы использовать, возможно, я мог бы участвовать в реализации.

...