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



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



system("ls")
567   15  

15 ответов:

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

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

output = `ls`
p output

или

printf output # escapes newline chars

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

насколько я могу сказать только 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 сообщение в блоге Джей Филдс подробно объясняет различия между использованием 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}'`                            # BAD

The 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 " в 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

puts `date`
puts $?


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

Comments

    Ничего не найдено.