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

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 с внешними программными фильтрами в общем виде.

4b9b3361

Ответ 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, который вы хотели бы использовать, возможно, я мог бы участвовать в реализации.