rails - Вызов команд оболочки из Ruby




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


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

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )

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

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

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

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


Дана команда, например, attrib

require 'open3'

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

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

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html


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

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

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

Официальная here имеет достаточно хороших примеров.

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)

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

Когда ты:

  • нужен как 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.


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

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

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

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

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

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

Не забудьте команду 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 но он не ожидает завершения команды.


Не совсем ответ, но, возможно, кто-то найдет это полезным, и это касается этого.

При использовании 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() - все это всплывающее окно раздражает при использовании TK и rubyw.


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

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

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

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

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


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

reboot = `init 6`
puts reboot

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

$ irb 
system "echo Hi"
Hi
=> true

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

cmd = 'ls'
system(cmd)

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

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`




interop