2014-09-07 4 views
0

Я пытаюсь сделать exec процесс оболочки таким образом, что его стандартный вывод имеет префикс с идентификатором.Ruby - Использование Kernel.exec с настраиваемым STDOUT

Мой подход заключается в написании пользовательского IO объект, который повторно реализует write, передавая ей в качестве аргумента :outexec (документально под Process::spawn).

require "delegate" 

class PrefixedStdout < DelegateClass(IO) 
    def initialize(prefix, io) 
    @prefix = prefix 
    super(io) 
    end 

    def write(str) 
    super("#{@prefix}: #{str}") 
    end 
end 

pr_stdout = PrefixedStdout.new("my_prefix", $stdout) 

pr_stdout.write("hello\n")   # outputs "my_prefix: hello" 
exec("echo hello", out: pr_stdout) # outputs "hello" 

Как-то exec обходит PrefixedStdout#write и вызывая $stdout.write непосредственно. Как мне заставить exec использовать мой префиксный поток вывода в качестве его stdout?

+1

Я не думаю, что этот подход может работать - то, что передается вашему дочернему процессу, является базовым дескриптором файла unix. (вы можете прочитать из этого файлового дескриптора, а затем записать на нужный конечный результат, добавив, какие префиксы вам нужны) –

+0

Спасибо, я думаю, это имеет смысл. 'pr_stdout.fileno' равно 1, что является стандартным. Не могли бы вы немного расширить свое альтернативное решение? – rickyrickyrice

ответ

2

То, что сохраняется в другом процессе, является основным файловым дескриптором (или, скорее, они подключены под капотом), так как я прокомментировал это, я не думаю, что вы когда-нибудь получите записи в этот дескриптор, который будет проходить через ваш метод записи - exec заменяет текущий процесс новым.

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

Например, вы могли бы сделать

IO.pipe do |read_pipe, write_pipe| 
    fork do 
    exec("echo hello", out: write_pipe) 
    end 
    write_pipe.close 
    while line = read_pipe.gets 
    puts "prefix: #{line}" 
    end 
end 

You также может быть заинтересован в IO.popen, который обертывает часть этого.

+0

Проверьте имена переменных. – 7stud

+0

Упс, хорошо пятнистый. спасибо –

+0

Да, это действительно очевидно, почему он не работал, когда вы так выразились. Спасибо за предложение, что на самом деле кажется жизнеспособной альтернативой.Я также смотрел [Open3] (http://ruby-doc.org/stdlib-1.9.3/libdoc/open3/rdoc/Open3.html), чтобы разрешить чтение из stdout и stderr самостоятельно. – rickyrickyrice

0

Как-то Exec обходит PrefixedStdout # запись и вызов $ stdout.write

Взгляните на этот пример:

class MyIO < IO 
    def initialize(fd) 
    super 
    end 

    def write(str) 
    STDOUT.puts 'write called' 
    super 
    end 
end 

fd = IO.sysopen("data.txt", "w") 
io = MyIO.new(fd) 
io.write "goodbye\n" 

puts '---now with exec()...' 
exec("echo hello", :out => io) 

--output:-- 
write called 
---now with exec()... 

Теперь, как вы думаете, в файле data.txt?

спойлер:

$ кошка data.txt
привет

Так передавая объект IO в EXEC() 'работает', но не так, вы ожидали: exec() никогда не вызывает io.write() для записи вывода дочернего процесса в io. Вместо этого я предполагаю, что exec() получает файловый дескриптор для io, затем передает его на некоторый код C, который выполняет перенаправление на уровне системы выходного файла дочернего процесса в файл data.txt.

Нужно ли использовать exec()? Если нет:

prefix = "prefix: " 
cmd = 'echo hello' 
output = `#{cmd}` 
puts "#{prefix}#{output}" 

--output:-- 
prefix: hello 
+0

Спасибо за ответ. Я не могу сделать это так, потому что процессы, которые я создаю, долгое время, поэтому я хочу перехватить stdout в реальном времени, а не ждать завершения процесса. Я думаю, что ваши рассуждения о том, как 'exec' говорит с аргументом': out', являются правильными. – rickyrickyrice

+0

@ rickyrickyrice, ладно, тогда IO.pipe/IO.popen/popen3 (и все) предоставит необходимые вам инструменты. – 7stud

Смежные вопросы