Получение вывода вызовов system() в Ruby

Если я вызову команду с помощью Kernel # system в Ruby, как мне получить ее вывод?

system("ls")

Ответ 1

Я хотел бы немного расширить и уточнить chaos answer.

Если вы окружаете команду с обратными выводами, вам вообще не нужно (явно) call system(). Выходы выполняют команду и возвращают вывод в виде строки. Затем вы можете присвоить значение переменной:

output = `ls`
p output

или

printf output # escapes newline chars

Ответ 2

Помните, что все решения, в которых вы передаете строку, содержащую предоставленные пользователем значения, system, %x[] и т.д., небезопасны! Небезопасное на самом деле означает: пользователь может запускать код для запуска в контексте и со всеми разрешениями программы.

Насколько я могу сказать, только версии system и Open3.popen3 предоставляют безопасный/экранирующий вариант в Ruby 1.8. В Ruby 1.9 IO::popen также принимает массив.

Просто передайте каждую опцию и аргумент как массив для одного из этих вызовов.

Если вам нужен не только статус выхода, но и результат, вы, вероятно, захотите использовать Open3.popen3:

require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value

Обратите внимание, что форма блока будет автоматически закрывать stdin, stdout и stderr, иначе они должны быть закрыты явно.

Дополнительная информация здесь: Формирование команд санитарной оболочки или системных вызовов в Ruby

Ответ 3

Только для записи, если вы хотите как (результат вывода, так и результат операции), вы можете сделать:

output=`ls no_existing_file` ;  result=$?.success?

Ответ 4

Вы можете использовать system() или% x [] в зависимости от того, какой результат вам нужен.

system() возвращает true, если команда найдена и успешно запущена, в противном случае - false.

>> s = system 'uptime'
10:56  up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14
=> true
>> s.class
=> TrueClass
>> $?.class
=> Process::Status

% x [..], с другой стороны, сохраняет результаты команды в виде строки:

>> result = %x[uptime]
=> "13:16  up 4 days,  1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> p result 
"13:16  up 4 days,  1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> result.class
=> String

Th запись в блоге Jay Fields подробно объясняет различия между использованием системы, exec и% x [..].

Ответ 5

Простой способ сделать это правильно и надежно - использовать Open3.capture2(), Open3.capture2e(), или Open3.capture3().

Использование обратных ссылок ruby ​​и его псевдоним %x НЕ БЕЗОПАСНО ПРИ ЛЮБЫХ ОБСТОЯТЕЛЬСТВАХ, если они используются с ненадежными данными. Он ОПАСНЫЙ, простой и простой:

untrusted = "; date; echo"
out = `echo #{untrusted}`                              # BAD

untrusted = '"; date; echo"'
out = `echo "#{untrusted}"`                            # BAD

untrusted = "'; date; echo'"
out = `echo '#{untrusted}'`                            # BAD

Функция system, напротив, правильно экранирует аргументы при правильном использовании:

ret = system "echo #{untrusted}"                       # BAD
ret = system 'echo', untrusted                         # good

Проблема в том, что он возвращает код выхода вместо вывода, а захват последнего запутан и запутан.

Лучший ответ в этом потоке упоминает Open3, но не функции, которые лучше всего подходят для задачи. Open3.capture2, capture2e и capture3 работают как system, но возвращают два или три аргумента:

out, err, st = Open3.capture3("echo #{untrusted}")     # BAD
out, err, st = Open3.capture3('echo', untrusted)       # good
out_err, st  = Open3.capture2e('echo', untrusted)      # good
out, st      = Open3.capture2('echo', untrusted)       # good
p st.exitstatus

Другой упоминает IO.popen(). Синтаксис может быть неуклюжим в том смысле, что он хочет получить массив как входной, но он тоже работает:

out = IO.popen(['echo', untrusted]).read               # good

Для удобства вы можете заключить Open3.capture3() в функцию, например:

#
# Returns stdout on success, false on failure, nil on error
#
def syscall(*cmd)
  begin
    stdout, stderr, status = Open3.capture3(*cmd)
    status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol
  rescue
  end
end

Пример:

p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')

Допускается следующее:

nil
nil
false
false
/usr/bin/which         <— stdout from system('which', 'which')
true                   <- p system('which', 'which')
"/usr/bin/which"       <- p syscall('which', 'which')

Ответ 6

Вы используете обратные ссылки:

`ls`

Ответ 7

Другой способ:

f = open("|ls")
foo = f.read()

Обратите внимание, что символ "pipe" перед "ls" открыт. Это также можно использовать для подачи данных на стандартный ввод программ, а также для чтения его стандартного вывода.

Ответ 8

Если вам нужно избежать аргументов, в Ruby 1.9 IO.popen также принимает массив:

p IO.popen(["echo", "it escaped"]).read

В более ранних версиях вы можете использовать Open3.popen3:

require "open3"

Open3.popen3("echo", "it escaped") { |i, o| p o.read }

Если вам также нужно пройти stdin, это должно работать как в версиях 1.9, так и в версии 1.8:

out = IO.popen("xxd -p", "r+") { |io|
    io.print "xyz"
    io.close_write
    io.read.chomp
}
p out # "78797a"

Ответ 9

Я обнаружил, что следующее полезное, если вам нужно возвращаемое значение:

result = %x[ls]
puts result

Я специально хотел перечислить pids всех процессов Java на моей машине и использовал это:

ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]

Ответ 10

Как Саймон Хюрлиманн уже объяснил, Open3 безопаснее, чем обратные ссылки и т.д.

require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }

Обратите внимание, что форма блока будет автоматически закрывать stdin, stdout и stderr, иначе они должны быть закрыто явно.

Ответ 11

Хотя использование обратных кавычек или popen часто является тем, что вам действительно нужно, на самом деле оно не отвечает на заданный вопрос. Могут быть веские причины для захвата вывода system (может быть, для автоматического тестирования). Небольшой Гугл нашел ответ, который, как я думал, я опубликую здесь для блага других.

Так как мне нужно было это для тестирования, мой пример использует настройку блока для захвата стандартного вывода, так как фактический system вызов скрыт в тестируемом коде:

require 'tempfile'

def capture_stdout
  stdout = $stdout.dup
  Tempfile.open 'stdout-redirect' do |temp|
    $stdout.reopen temp.path, 'w+'
    yield if block_given?
    $stdout.reopen stdout
    temp.read
  end
end

Этот метод захватывает любой вывод в данном блоке, используя временный файл для хранения фактических данных. Пример использования:

captured_content = capture_stdout do
  system 'echo foo'
end
puts captured_content

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

Ответ 12

Если вы хотите, чтобы выход перенаправлялся в файл с помощью Kernel#system, вы можете изменить дескрипторы следующим образом:

перенаправить stdout и stderr в файл (/tmp/log) в режиме добавления:

system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])

Для длинной команды это сохранит вывод в режиме реального времени. Вы также можете сохранить вывод с помощью IO.pipe и перенаправить его из системы Kernel #.

Ответ 14

Я не нашел этого здесь, поэтому добавив его, у меня возникли проблемы с получением полной информации.

Вы можете перенаправить STDERR на STDOUT, если хотите захватить STDERR, используя кавычка.

output = `grep hosts/private/etc/* 2 > & 1`

источник: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

Ответ 15

puts `date`
puts $?


Mon Mar  7 19:01:15 PST 2016
pid 13093 exit 0