Если я вызову команду с помощью Kernel # system в Ruby, как мне получить ее вывод?
system("ls")
Если я вызову команду с помощью Kernel # system в Ruby, как мне получить ее вывод?
system("ls")
Я хотел бы немного расширить и уточнить chaos answer.
Если вы окружаете команду с обратными выводами, вам вообще не нужно (явно) call system(). Выходы выполняют команду и возвращают вывод в виде строки. Затем вы можете присвоить значение переменной:
output = `ls`
p output
или
printf output # escapes newline chars
Помните, что все решения, в которых вы передаете строку, содержащую предоставленные пользователем значения, 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
Только для записи, если вы хотите как (результат вывода, так и результат операции), вы можете сделать:
output=`ls no_existing_file` ; result=$?.success?
Вы можете использовать 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 [..].
Простой способ сделать это правильно и надежно - использовать 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')
Вы используете обратные ссылки:
`ls`
Другой способ:
f = open("|ls")
foo = f.read()
Обратите внимание, что символ "pipe" перед "ls" открыт. Это также можно использовать для подачи данных на стандартный ввод программ, а также для чтения его стандартного вывода.
Если вам нужно избежать аргументов, в 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"
Я обнаружил, что следующее полезное, если вам нужно возвращаемое значение:
result = %x[ls]
puts result
Я специально хотел перечислить pids всех процессов Java на моей машине и использовал это:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
Как Саймон Хюрлиманн уже объяснил, Open3 безопаснее, чем обратные ссылки и т.д.
require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
Обратите внимание, что форма блока будет автоматически закрывать stdin, stdout и stderr, иначе они должны быть закрыто явно.
Хотя использование обратных кавычек или 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
если хотите.
Если вы хотите, чтобы выход перенаправлялся в файл с помощью Kernel#system
, вы можете изменить дескрипторы следующим образом:
перенаправить stdout и stderr в файл (/tmp/log) в режиме добавления:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
Для длинной команды это сохранит вывод в режиме реального времени. Вы также можете сохранить вывод с помощью IO.pipe и перенаправить его из системы Kernel #.
В качестве прямой замены системы (...) вы можете использовать Open3.popen3 (...)
Дальнейшее обсуждение: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
Я не нашел этого здесь, поэтому добавив его, у меня возникли проблемы с получением полной информации.
Вы можете перенаправить STDERR на STDOUT, если хотите захватить STDERR, используя кавычка.
output = `grep hosts/private/etc/* 2 > & 1`
источник: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
puts `date`
puts $?
Mon Mar 7 19:01:15 PST 2016
pid 13093 exit 0