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














Я определенно не эксперт Руби, но я дам ему выстрелили:
$ irb
system "echo Hi"
Hi
=> true
Вы также должны быть в состоянии делать такие вещи, как:
cmd = 'ls'
system(cmd)
Вы также можете использовать операторы кавычки ( `), похожий на Perl:
directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory
Удобно, если вам нужно что-то простое.
Какой метод вы хотите использовать, зависит от того, что вы пытаетесь достичь; проверьте документы для получения более подробной информации о различных методах.
Как я хотел бы сделать это с помощью %x
литерала, что делает его легко использовать кавычки в команде, например , так (и читаемым!):
directorylist = %x[find . -name '*test.rb' | sort]
Что, в этом случае, будет заполнять список файлов со всех тестовых файлов в текущем каталоге, который можно обрабатывать, как и ожидалось:
directorylist.each do |filename|
filename.chomp!
# work with file
end

Это объяснение основано на комментировали Руби скрипт из моего друга. Если вы хотите улучшить сценарий, не стесняйтесь , чтобы обновить его по ссылке.
Во- первых, обратите внимание , что , когда Руби взывает к оболочке, это обычно вызывает /bin/sh
, не Bash. Некоторые синтаксиса Bash не поддерживается /bin/sh
на всех системах.
Вот способы, чтобы выполнить скрипт:
cmd = "echo 'hi'" # Sample string that can be used
Kernel#`
, Который обычно называют обратными кавычками -`cmd`
Это, как и многие другие языки, в том числе Bash, PHP и Perl.
Возвращает результат команды оболочки.
Документы: http://ruby-doc.org/core/Kernel.html#method-i-60
value = `echo 'hi'` value = `#{cmd}`
Встроенный в синтаксисе,
%x( cmd )
После
x
символа является разделителем, который может быть любым символом. Если разделитель является одним из символов(
,[
,{
или<
, в буквальном смысле состоит из символов , до закрытия соответствия разделителя, с учетом вложенных пар разделителей. Для всех других разделителей, буквальные содержат символы до следующего вхождения символа - ограничителя. Строка интерполяции#{ ... }
допускается.Возвращает результат команды оболочки, так же как и обратные кавычки.
Документы: http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html
value = %x( echo 'hi' ) value = %x[ #{cmd} ]
Kernel#system
Выполняет указанную команду в субоболочке.
Возвращает ,
true
если команда была найдена и побежал успешно, вfalse
противном случае.Документы: http://ruby-doc.org/core/Kernel.html#method-i-system
wasGood = system( "echo 'hi'" ) wasGood = system( cmd )
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
Для получения дополнительного чтения смотрите:
Некоторые вещи, чтобы думать о том, когда выбор между этими механизмами являются:
- Вы просто хотите STDOUT или вам нужно STDERR, а? или даже отделяется?
- Насколько велик ваш вывод? Вы хотите провести весь результат в памяти?
- Вы хотите, чтобы прочитать некоторые из вашего выхода в то время как подпроцесс все еще работает?
- Вам необходимы результирующие коды?
- Вам нужен объект рубина, который представляет этот процесс и позволяет убить его по требованию?
Возможно , вам что - нибудь из простых обратных кавычек ( ``), система (), и IO.popen
в полномасштабный Kernel.fork
/ Kernel.exec
с IO.pipe
и IO.select
.
Вы также можете бросить тайм-ауты в смесь, если подпроцесс занимает слишком много времени, чтобы выполнить.
К сожалению, это очень зависит .
Вот лучшая статья на моем взгляде , о запуске скриптов в Ruby: « 6 способов для запуска команд оболочки в Рубине ».
Если вам нужно только, чтобы получить выход на использование кавычку.
Мне нужно было более продвинутые вещи, как STDOUT и STDERR, так что я использовал Open4 камень. У вас есть все методы объяснили там.
Мой любимый open3
require "open3"
Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
Еще один вариант:
Когда ты:
- нужно STDERR, а также стандартный вывод
- не может / не будет использовать 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.
Мы можем добиться этого несколько способов.
Не используя Kernel#exec
, ничего после выполнения этой команды:
exec('ls ~')
С помощью backticks or %x
`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"
Использование Kernel#system
команды возвращает в true
случае успеха, false
если неудачно и возвращается , nil
если ошибка выполнения команды:
system('ls ~')
=> true
Используя ответы здесь и связаны в ответ Михая, я соединял функцию, которая отвечает следующим требованиям:
- Аккуратные захватывает STDOUT и STDERR, чтобы они не «утечка», когда мой скрипт запускается из консоли.
- Позволяет аргументы, которые будут переданы в оболочку как массив, так что нет необходимости беспокоиться о побеге.
- Захватывает статус завершения команды, так что ясно, когда произошла ошибка.
В качестве бонуса, это один будет также возвращать 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"
Ответы выше уже достаточно велик, но я действительно хочу поделиться следующей сводной статьи: « 6 способов выполнения команд оболочки в Рубине »
В основном, это говорит нам:
Kernel#exec
:
exec 'echo "hello $HOSTNAME"'
system
и $?
:
system 'false'
puts $?
Обратные кавычки ( `):
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>]
Вот крутой один, который я использую в рубиновый скрипт на OS X (так что я могу запустить скрипт и получить обновление, даже после того, переключаясь от окна):
cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Не забудьте 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
но не ждать завершения работы команды.
Если у вас есть более сложный случай , чем общий случай (который не может быть обработан с ``
) , а затем проверить Kernel.spawn()
здесь . Это , как представляется, наиболее общие / полнофункциональный предусмотрено акций Рубин для выполнения внешних команд.
Например, вы можете использовать его для:
- создавать группы процессов (Windows)
- перенаправлять, выдох, ошибки в файлах / друг-друга.
- Набор окр вары, Umask
- изменить реж перед выполнением команды
- ограничения набора ресурсов для CPU / данных / ...
- Делайте все, что можно сделать с другими вариантами в других ответах, но с большим количеством коды.
Официальная документация рубин имеет достаточно хорошие примеры.
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)
Вот блок - схема алгоритма на основе этого ответа . Смотрите также, используя script
для эмуляции терминала .
Метод `обратных кавычек является самым простым один для вызова команд оболочки из рубина. Она возвращает результат выполнения команды оболочки.
url_request = 'http://google.com' result_of_shell_command = `curl #{url_request}`
Самый простой способ, например:
reboot = `init 6`
puts reboot
Если вам действительно нужно Bash, в записке в «лучшем» ответ.
Во- первых, обратите внимание , что , когда Руби взывает к оболочке, это обычно вызывает
/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
, вы , вероятно , не заметите.
Учитывая команду, например Attrib
require 'open3'
a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
puts stdout.read
end
Я обнаружил, что в то время как этот метод не является столь запоминающимся, как, например, системы ( «thecommand») или thecommand в обратных кавычках, хорошая вещь об этом методе по сравнению с другими методами .. это например, обратные кавычки, кажется, не дайте мне «путы 'команда бегает / сберегать команду я хочу запустить в переменном, и системе ( „thecommand“), кажется, не дайте мне получить выход. В то время как этот метод позволяет мне делать оба этих вещи, и это позволяет мне стандартный ввод доступа, стандартный вывод и стандартный поток ошибок независимо друг от друга.
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
Не совсем ответ, но, возможно, кто-то найдет это полезным, и его отношении к этому.
При использовании ТЗ 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», но никаких окон не придумают.
U нужно будет require 'win32ole'
внутри вашего скрипта.
system()
, exec()
И spawn()
все будет всплывал , что раздражает окно при использовании ТЗ и rubyw.