Я знаю, что это старая ветка, но есть еще один вариант, который был слегка затронут Саймон Хюрлиманн .
По этой теме не так много информации, и я думаю,это может помочь другим нуждающимся.
В этом примере мы будем использовать Open3
, который дает вам возможность выполнять команды синхронно или асинхронно и обеспечивает stdout , stderr , коды выхода и PID .
Open3 предоставляет вам доступ к stdout, stderr, кодам выхода и потоку для ожидания дочернего процесса при запуске другогопрограмма.Вы можете указать различные атрибуты, перенаправления, текущий каталог и т. Д. Программы так же, как и для Process.spawn.( Источник: Open3 Docs )
Я решил отформатировать вывод как CommandStatus
объект.Это содержит наши stdout
, stderr
, pid
(из рабочего потока) и exitstatus
.
class Command
require 'open3'
class CommandStatus
@stdout = nil
@stderr = nil
@pid = nil
@exitstatus = nil
def initialize(stdout, stderr, process)
@stdout = stdout
@stderr = stderr
@pid = process.pid
@exitstatus = process.exitstatus
end
def stdout
@stdout
end
def stderr
@stderr
end
def exit_status
@exitstatus
end
def pid
@pid
end
end
def self.execute(command)
command_stdout = nil
command_stderr = nil
process = Open3.popen3(ENV, command + ';') do |stdin, stdout, stderr, thread|
stdin.close
stdout_buffer = stdout.read
stderr_buffer = stderr.read
command_stdout = stdout_buffer if stdout_buffer.length > 0
command_stderr = stderr_buffer if stderr_buffer.length > 0
thread.value # Wait for Process::Status object to be returned
end
return CommandStatus.new(command_stdout, command_stderr, process)
end
end
cmd = Command::execute("echo {1..10}")
puts "STDOUT: #{cmd.stdout}"
puts "STDERR: #{cmd.stderr}"
puts "EXIT: #{cmd.exit_status}"
При чтении буферов STDOUT / ERR я использую command_stdout = stdout_buffer if stdout_buffer.length > 0
, чтобы управлять назначением переменной command_stdout
или нет.Вы должны передать nil
вместо ""
, если данные отсутствуют.Это более понятно при передаче данных позже.
Вы, вероятно, заметили, что я использовал command + ';'
.Причина этого основана на документации из Kernel.exec (которая используется в popen3):
Если строка из первой формы (exec ("command")) следует этим простым правилам:
- без метасимволов
- без зарезервированного для оболочки слова и без специального встроенного
- Ruby вызывает команду напрямую без оболочки
Вы можете принудительно вызвать оболочку, добавив «;»в строку (потому что «;» является метасимволом)
Это просто не дает Ruby выдать ошибку 'spawn': No such file or directory
, если вы передаете искаженную команду.Вместо этого он передаст его прямо в ядро, где ошибка будет исправлена и будет отображаться как STDERR вместо необработанного исключения.