15 ответов:
Я хотел бы расширить и уточнить хаос немного.
Если вы окружаете свою команду обратными кавычками, то вам вообще не нужно (явно) вызывать system (). Обратные палочки выполняют команду и возвращают выходные данные в виде строки. Затем вы можете присвоить значение переменной следующим образом:
output = `ls` p outputили
printf output # escapes newline chars
имейте в виду, что все решения, в которых вы передаете строку, содержащую пользовательские значения в
system,%x[]etc. небезопасны! Небезопасно на самом деле означает: пользователь может вызвать код для запуска в контексте и со всеми разрешениями программы.насколько я могу сказать только
systemиOpen3.popen3обеспечьте безопасный / экранирующий вариант в Ruby 1.8. В Ruby 1.9IO::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 => StringTh сообщение в блоге Джей Филдс подробно объясняет различия между использованием system, exec и %x[..] .
простой способ сделать это правильно и безопасно, чтобы использовать
Open3.capture2(),Open3.capture2e()илиOpen3.capture3().используя обратные ходы Руби и его
%xпсевдоним НЕ БЕЗОПАСНО НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ если используется с ненадежными данными. Это опасно все просто:untrusted = "; date; echo" out = `echo #{untrusted}` # BAD untrusted = '"; date; echo"' out = `echo "#{untrusted}"` # BAD untrusted = "'; date; echo'" out = `echo '#{untrusted}'` # BADThe
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')
другой способ:
f = open("|ls") foo = f.read()обратите внимание, что это символ" pipe "перед" ls " в open. Это также может быть использовано для подачи данных в программы стандартного ввода, а также чтения его стандартного вывода.
Если вам нужно избежать аргументов, в Ruby 1.9 IO.попен также принимает массив:
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"
я обнаружил, что следующее полезно, если вам нужно возвращаемое значение:
result = %x[ls] puts resultЯ специально хотел перечислить pids всех процессов Java на моей машине и использовал это:
ids = %x[ps ax | grep java | awk '{ print }' | xargs]
Как Симон Hürlimann уже объяснил,Open3 безопаснее, чем backticks и т. д.
require 'open3' output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }обратите внимание, что форма блока будет автоматически закрывать stdin, stdout и stderr-в противном случае они должны быть явно закрытая.
Если вы хотите перенаправить вывод в файл с помощью
Kernel#system, вы можете изменить дескрипторы следующим образом:перенаправить stdout и stderr в файл (/tmp / log) в режиме добавления:
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])для длительной команды, это будет хранить выходные данные в режиме реального времени. Вы также можете сохранить вывод с помощью ввода-вывода.труба и перенаправить его из ядра#системы.
при использовании backticks или 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таким образом, это дает нам метод, который будет захватывать любые выходные данные в данном блоке с помощью tempfile для хранения фактических данных. Пример использования:
captured_content = capture_stdout do system 'echo foo' end puts captured_contentконечно, вы можете заменить
systemзвоните со всем, что может внутренне вызватьsystem. Вы также можете использовать тот же метод для захвата stderr, если хотите.
как прямая система(...) замены вы можете использовать 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
Comments