Как вызывать команды оболочки из Ruby

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

5.08.2008 12:56:52
Хотя этот вопрос полезен, он не очень хорошо задан. В Ruby есть много способов вызова вложенных оболочек, которые хорошо документированы и легко найдены, прочитав документацию по Kernel и Open3 и выполнив поиск здесь в SO.
the Tin Man 19.04.2017 23:21:04
К сожалению, эта тема довольно сложная. Open3( docs ) - лучший выбор для большинства ситуаций, IMO, но в более старых версиях Ruby он не учитывает измененные PATH( bugs.ruby-lang.org/issues/8004 ) и зависит от того, как вы передаете аргументы (в частности, , если вы используете хэш opts с не ключевыми словами), он может сломаться. Но если вы попадаете в такие ситуации, вы делаете что-то довольно продвинутое, и вы можете понять, что делать, прочитав реализацию Open3.
Joshua Cheek 22.05.2018 18:47:34
Я удивлен, что никто не упомянул Shellwords.escape( док ). Вы не хотите вставлять пользовательский ввод непосредственно в команды оболочки - сначала удалите его! Смотрите также командный впрыск .
Kelvin 6.12.2018 17:31:53
20 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` обычно называют backticks - `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

Для получения дополнительной информации см .:

1316
5.09.2019 15:44:51
Мне нужно записать результаты моего исполняемого файла на производственном сервере, но не нашел пути. Я использовал #{cmd}put и logger.info ( #{cmd}). Есть ли способ зарегистрировать свои выходы на производстве?
Omer Aslam 1.10.2012 17:37:18
И IO # popen () и Open3 # popen3 (). mentalized.net/journal/2010/03/08/...
hughdbrown 6.02.2013 23:20:46
Для полноты (как я сначала подумал, это также будет команда Ruby): Rake имеет sh, который выполняет команду «Выполнить системную команду cmd. Если задано несколько аргументов, команда не запускается с оболочкой (семантика такая же, как у Kernel ::». exec и Kernel :: system) ".
sschuberth 15.02.2013 16:19:44
Обратные пометки не захватывают STDERR по умолчанию. Добавьте `2> & 1` в команду, если хотите захватить
Andrei Botalov 17.02.2013 22:19:50
Я думаю, что этот ответ был бы немного улучшен, если бы он сказал, что обратные пометки и% x вернули «результат», а не «результат» данной команды. Последний может быть ошибочно принят за статус выхода. Или это только я?
skagedal 12.03.2015 15:05:10

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

$ irb 
system "echo Hi"
Hi
=> true

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

cmd = 'ls'
system(cmd)
25
11.10.2013 01:12:02

Вы также можете использовать операторы backtick (`), аналогичные Perl:

directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory

Удобно, если вам нужно что-то простое.

Какой метод вы хотите использовать, зависит от того, что именно вы пытаетесь достичь; проверьте документы для более подробной информации о различных методах.

11
5.08.2008 13:57:47

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

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

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

directorylist.each do |filename|
  filename.chomp!
  # work with file
end
160
10.01.2013 05:42:55
Имеет ли %x[ cmd ]возвращает массив для вас?
x-yuri 2.06.2014 13:24:18
вышеупомянутое не работает для меня. `` <main> ': неопределенный метод, each' for :String (NoMethodError) как он работал для вас? Я использую ruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]Вы уверены, что массив возвращается из команды, чтобы цикл действительно работал?
Nasser 22.06.2015 20:24:53
% x [cmd] .split ("\ n") вернет список, хотя :)
Ian Ellis 7.12.2016 20:28:18

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

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

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

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

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

29
10.01.2020 01:47:38

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

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

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

65
11.10.2013 01:11:06
Пост, описанный здесь, не обсуждает %xопцию синтаксиса.
Mei 22.11.2011 17:58:02
+1 для Open4. Я уже начал пытаться реализовать свою собственную версию его spawnметода, когда нашел это.
Brandan 19.11.2012 22:02:37
@MihaiA - у меня небольшая проблема с состоянием соединения ssh. Пожалуйста, помогите мне - stackoverflow.com/questions/28799593/… спасибо.
james 2.03.2015 01:00:12

Мой любимый это Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
39
13.07.2012 02:34:07
Мне также нравится open3, особенно Open3.capture3: ruby-doc.org/stdlib-1.9.3/libdoc/open3/rdoc/… -> stdout, stderr, status = Open3.capture3('nroff -man', :stdin_data => stdin)
severin 6.05.2013 07:39:57
Есть ли в Ruby std-lib документация о том, как выполнять Spec и модульное тестирование с помощью Open3 или других Open? На моем нынешнем уровне понимания сложно протестировать оболочки.
FilBot3 29.11.2016 15:30:15

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

Когда ты:

  • нужен как stderr, так и stdout
  • не могу / не буду использовать 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.

25
11.10.2013 01:10:31

Мы можем достичь этого несколькими способами.

Используя Kernel#exec, ничего после этой команды не выполняется:

exec('ls ~')

С помощью backticks or %x

`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"

Использование Kernel#systemкоманды возвращает в trueслучае успеха, falseесли не удалось, и возвращает в nilслучае сбоя выполнения команды:

system('ls ~')
=> true
10
11.10.2013 01:10:04

Используя ответы здесь и связанные в ответе Михая, я собрал функцию, которая отвечает этим требованиям:

  1. Аккуратно фиксирует STDOUT и STDERR, чтобы они не «просачивались», когда мой скрипт запускается из консоли.
  2. Позволяет передавать аргументы в оболочку в виде массива, поэтому вам не нужно беспокоиться об экранировании.
  3. Записывает состояние завершения команды, чтобы было ясно, когда произошла ошибка.

В качестве бонуса, этот также вернет STDOUT в тех случаях, когда команда оболочки успешно завершает работу (0) и помещает что-либо в STDOUT. Этим он отличается от того system, который просто возвращается trueв таких случаях.

Кодекс следует. Конкретная функция system_quietly:

require 'open3'

class ShellError < StandardError; end

#actual function:
def system_quietly(*cmd)
  exit_status=nil
  err=nil
  out=nil
  Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
    err = stderr.gets(nil)
    out = stdout.gets(nil)
    [stdin, stdout, stderr].each{|stream| stream.send('close')}
    exit_status = wait_thread.value
  end
  if exit_status.to_i > 0
    err = err.chomp if err
    raise ShellError, err
  elsif out
    return out.chomp
  else
    return true
  end
end

#calling it:
begin
  puts system_quietly('which', 'ruby')
rescue ShellError
  abort "Looks like you don't have the `ruby` command. Odd."
end

#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
9
11.10.2013 01:09:02

Ответы выше уже довольно велики, но я действительно хочу поделиться следующей сводной статьей: « 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>]
21
28.10.2019 12:21:25

Вот классный пример, который я использую в скрипте ruby ​​на OS X (чтобы я мог запустить скрипт и получить обновление даже после переключения из окна):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
-2
14.10.2014 20:12:11

Не забудьте spawnкоманду для создания фонового процесса для выполнения указанной команды. Вы даже можете дождаться его завершения, используя Processкласс и возвращаемое значение pid:

pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid

pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid

Док говорит: «Этот метод похож на, #systemно он не ждет завершения команды.

9
4.11.2015 15:04:26
Kernel.spawn()кажется гораздо более универсальным, чем все другие варианты.
Kashyap 11.12.2015 14:24:03

Если у вас есть более сложный случай, чем общий случай, с которым невозможно ``разобраться, проверьте Kernel.spawn(). Похоже, это наиболее универсальный / полнофункциональный инструмент, предоставляемый стандартным Ruby для выполнения внешних команд.

Вы можете использовать его для:

  • создать группы процессов (Windows).
  • перенаправлять ошибки, файлы / друг друга.
  • установить env vars, umask.
  • измените каталог перед выполнением команды.
  • установить ограничения ресурсов для процессора / данных / и т. д.
  • Делайте все, что можно сделать с другими вариантами в других ответах, но с большим количеством кода.

Документация Ruby имеет достаточно хороших примеров:

env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
command...:
  commandline                 : command line string which is passed to the standard shell
  cmdname, arg1, ...          : command name and one or more arguments (no shell)
  [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
  clearing environment variables:
    :unsetenv_others => true   : clear environment variables except specified by env
    :unsetenv_others => false  : dont clear (default)
  process group:
    :pgroup => true or 0 : make a new process group
    :pgroup => pgid      : join to specified process group
    :pgroup => nil       : dont change the process group (default)
  create new process group: Windows only
    :new_pgroup => true  : the new process is the root process of a new process group
    :new_pgroup => false : dont create a new process group (default)
  resource limit: resourcename is core, cpu, data, etc.  See Process.setrlimit.
    :rlimit_resourcename => limit
    :rlimit_resourcename => [cur_limit, max_limit]
  current directory:
    :chdir => str
  umask:
    :umask => int
  redirection:
    key:
      FD              : single file descriptor in child process
      [FD, FD, ...]   : multiple file descriptor in child process
    value:
      FD                        : redirect to the file descriptor in parent process
      string                    : redirect to file with open(string, "r" or "w")
      [string]                  : redirect to file with open(string, File::RDONLY)
      [string, open_mode]       : redirect to file with open(string, open_mode, 0644)
      [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
      [:child, FD]              : redirect to the redirected file descriptor
      :close                    : close the file descriptor in child process
    FD is one of follows
      :in     : the file descriptor 0 which is the standard input
      :out    : the file descriptor 1 which is the standard output
      :err    : the file descriptor 2 which is the standard error
      integer : the file descriptor of specified the integer
      io      : the file descriptor specified as io.fileno
  file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
    :close_others => false : inherit fds (default for system and exec)
    :close_others => true  : dont inherit (default for spawn and IO.popen)
6
10.01.2020 01:54:20
Вау хаха Очень полезно, хотя тот факт, что это должно существовать, вызывает сожаление
Josh Bodah 19.12.2016 16:01:30

Метод backticks (`) является самым простым для вызова команд оболочки из Ruby. Возвращает результат команды оболочки:

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
6
10.01.2020 01:55:02

Самый простой способ, например:

reboot = `init 6`
puts reboot
10
10.01.2020 01:51:16

Если вам действительно нужен 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")

Руби должен соблюдать шебанг, но вы всегда можете использовать

system("bash ./my_script.sh")

чтобы убедиться, что при /bin/shзапуске могут быть небольшие накладные расходы /bin/bash, вы, вероятно, этого не заметите.

15
10.01.2020 01:50:51

Дана команда типа attrib:

require 'open3'

a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
  puts stdout.read
end

Я обнаружил, что хотя этот метод не так запоминающийся, как

system("thecommand")

или

`thecommand`

в обратном трюке хорошая вещь в этом методе по сравнению с другими методами состоит в том, что обратные трюки, кажется, не позволяют мне putsзапустить команду / сохранить команду, которую я хочу запустить в переменной, и system("thecommand"), похоже, не позволяют мне получить вывод, тогда как этот метод позволяет мне делать обе эти вещи, и он дает мне доступ к stdin, stdout и stderr независимо.

См. « Выполнение команд в ruby » и документацию по Ruby для Open3 .

5
10.01.2020 01:59:27

Это не совсем ответ, но, возможно, кто-то найдет его полезным:

При использовании TK GUI в Windows и необходимости вызывать команды оболочки из rubyw у вас всегда будет раздражающее окно CMD, появляющееся менее чем за секунду.

Чтобы избежать этого, вы можете использовать:

WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)

или

WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)

Оба будут хранить ipconfigвыходные данные внутри log.txt, но окна не появятся.

Вам нужно будет require 'win32ole'внутри вашего сценария.

system(), exec()И spawn()будет все всплывающие окна , что раздражает при использовании ТЗ и rubyw.

3
10.01.2020 02:28:19