Вызов команд оболочки из Ruby - PullRequest
976 голосов
/ 05 августа 2008

Как мне вызывать команды оболочки внутри Ruby-программы? Как я могу получить выходные данные этих команд обратно в Ruby?

Ответы [ 20 ]

1238 голосов
/ 05 августа 2008

Это объяснение основано на комментариях Ruby-скрипта от моего друга. Если вы хотите улучшить скрипт, обновите его по ссылке.

Во-первых, обратите внимание, что когда Ruby вызывает оболочку, он обычно вызывает /bin/sh, , а не Bash. Некоторый синтаксис Bash не поддерживается /bin/sh во всех системах.

Вот способы выполнения сценария оболочки:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#`, обычно называемые обратными чертами - `cmd`

    Это как и многие другие языки, включая Bash, PHP и Perl.

    Возвращает результат команды оболочки.

    Документы: http://ruby -doc.org / core / Kernel.html # method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. Встроенный синтаксис, %x( cmd )

    После символа x стоит разделитель, который может быть любым символом. Если разделителем является один из символов (, [, { или <, литерал состоит из символов до соответствующего закрывающего разделителя, с учетом вложенных пар разделителей. Для всех других разделителей литерал включает в себя символы до следующего появления разделитель символа. Допускается интерполяция строк #{ ... }.

    Возвращает результат команды оболочки, так же как обратные пометки.

    Документы: http://www.ruby -doc.org / docs / ProgrammingRuby / html / language.html

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
  3. Kernel#system

    Выполняет данную команду в подоболочке.

    Возвращает true, если команда была найдена и успешно выполнена, false в противном случае.

    Документы: http://ruby -doc.org / core / Kernel.html # method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. Kernel#exec

    Заменяет текущий процесс, выполнив данную внешнюю команду.

    Не возвращает ничего, текущий процесс заменяется и никогда не продолжается.

    Документы: http://ruby -doc.org / core / Kernel.html # method-i-exec

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above
    

Вот несколько дополнительных советов: $?, что совпадает с $CHILD_STATUS, получает доступ к состоянию последней выполненной системой команды, если вы используете обратные метки, system() или %x{}. Затем вы можете получить доступ к свойствам exitstatus и pid:

$?.exitstatus

Подробнее читайте в:

213 голосов
/ 19 мая 2016

Вот блок-схема, основанная на этом ответе . См. Также, с использованием script для эмуляции терминала .

enter image description here

156 голосов
/ 05 августа 2008

Мне нравится делать это, используя литерал %x, который позволяет легко (и удобно читать) использовать кавычки в команде, например:

directorylist = %x[find . -name '*test.rb' | sort]

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

directorylist.each do |filename|
  filename.chomp!
  # work with file
end
60 голосов
/ 02 сентября 2008

Вот лучшая на мой взгляд статья о запуске сценариев оболочки в Ruby: " 6 способов запуска команд оболочки в Ruby ".

Если вам нужно только получить выходные данные, используйте обратные метки.

Мне нужны были более продвинутые вещи, такие как STDOUT и STDERR, поэтому я использовал камень Open4. У вас есть все методы, объясненные там.

32 голосов
/ 18 сентября 2008

Мой любимый Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
23 голосов
/ 07 августа 2008

При выборе между этими механизмами следует учитывать следующие факторы:

  1. Вы просто хотите использовать стандартный вывод? нужен также stderr? или даже выделены?
  2. Насколько велика ваша продукция? Вы хотите держать весь результат в памяти?
  3. Вы хотите прочитать некоторые из ваших выводить, пока подпроцесс еще работает?
  4. Вам нужны коды результатов?
  5. Вам нужен рубиновый объект, который представляет процесс и позволяет вам убить его по требованию?

Вам может понадобиться что угодно, от простых обратных галочек (``), system () и IO.popen до полноценных Kernel.fork / Kernel.exec с IO.pipe и IO.select.

Вы также можете выбросить тайм-ауты в микс, если подпроцесс занимает слишком много времени для выполнения.

К сожалению, это очень сильно зависит .

20 голосов
/ 16 июня 2010

Еще один вариант:

Когда вы:

  • нужен стандартный вывод, а также стандартный вывод
  • не может / не будет использовать Open3 / Open4 (они генерируют исключения в NetBeans на моем Mac, не знаю почему)

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

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

Синтаксис 2>&1 работает в Linux , Mac и Windows с первых дней существования MS-DOS.

20 голосов
/ 05 августа 2008

Я определенно не эксперт по Ruby, но попробую:

$ irb 
system "echo Hi"
Hi
=> true

Вы также должны иметь возможность делать такие вещи, как:

cmd = 'ls'
system(cmd)
13 голосов
/ 07 июня 2013

Ответы выше уже довольно велики, но я действительно хочу поделиться следующей сводной статьей: " 6 способов запуска команд оболочки в Ruby "

В основном, это говорит нам:

Kernel#exec

exec 'echo "hello $HOSTNAME"'

system и $?:

system 'false' 
puts $?

Backticks (`):

today = `date`

IO#popen:

IO.popen("date") { |f| puts f.gets }

Open3#popen3 - stdlib:

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 - драгоценный камень:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]
10 голосов
/ 02 июня 2017

Если вам действительно нужен Bash, согласно примечанию в «лучшем» ответе.

Во-первых, обратите внимание, что когда Ruby вызывает оболочку, он обычно вызывает /bin/sh, , а не Bash. Некоторый синтаксис Bash не поддерживается /bin/sh во всех системах.

Если вам нужно использовать Bash, вставьте bash -c "your Bash-only command" в нужный вам метод вызова.

quick_output = system("ls -la")

quick_bash = system("bash -c 'ls -la'")

Для проверки:

system("echo $SHELL") system('bash -c "echo $SHELL"')

Или, если вы запускаете существующий файл сценария (например, script_output = system("./my_script.sh")), Ruby должен соблюдать шебанг, но вы всегда можете использовать system("bash ./my_script.sh"), чтобы убедиться (хотя могут быть небольшие издержки /bin/sh работает /bin/bash, вы, вероятно, не заметите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...