Получение вывода системных вызовов () в Ruby - PullRequest
296 голосов
/ 27 марта 2009

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

system("ls")

Ответы [ 15 ]

335 голосов
/ 14 октября 2009

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

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

output = `ls`
p output

или

printf output # escapes newline chars
234 голосов
/ 12 мая 2011

Имейте в виду, что все решения, в которых вы передаете строку, содержащую предоставленные пользователем значения 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

162 голосов
/ 17 октября 2010

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

output=`ls no_existing_file` ;  result=$?.success?
59 голосов
/ 15 ноября 2013

Простой способ сделать это правильно и безопасно - использовать 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() в функцию, например ::1010 *

#
# 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')
59 голосов
/ 25 ноября 2010

Вы можете использовать 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 сообщение в блоге Джея Филдса подробно объясняет различия между использованием system, exec и% x [..].

19 голосов
/ 04 июля 2012

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

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

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

require "open3"

Open3.popen3("echo", "it's 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"
19 голосов
/ 27 марта 2009

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

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

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

19 голосов
/ 27 марта 2009

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

`ls`
13 голосов
/ 02 марта 2010

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

result = %x[ls]
puts result

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

ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
11 голосов
/ 12 января 2014

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

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

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

...