Вызов команд оболочки из Ruby
499608 просмотра
20 ответа
Как мне вызвать команды оболочки изнутри программы на Ruby? Как я могу получить вывод этих команд обратно в Ruby?
Автор: CodingWithoutComments Источник Размещён: 05.09.2019 06:46Ответы (20)
1259 плюса
Это объяснение основано на комментируемом сценарии Ruby от моего друга. Если вы хотите улучшить скрипт, обновите его по ссылке.
Во-первых, обратите внимание, что когда Ruby вызывает оболочку, он обычно вызывает /bin/sh
, а не Bash. Некоторый синтаксис Bash поддерживается не /bin/sh
во всех системах.
Вот способы выполнения сценария оболочки:
cmd = "echo 'hi'" # Sample string that can be used
Kernel#`
обычно называют backticks -`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
Для получения дополнительной информации см .:
- http://www.elctech.com/blog/im-in-ur-commandline-executin-ma-commands
- http://blog.jayfields.com/2006/06/ruby-kernel-system-exec-and-x.html
- http://tech.natemurray.com/2007/03/ruby-shell-commands.html
227 плюса
Вот блок-схема, основанная на этом ответе . Смотрите также, используя script
для эмуляции терминала .
157 плюса
Мне нравится делать это, используя %x
литерал, который позволяет легко (и удобно читать) использовать кавычки в команде, например так:
directorylist = %x[find . -name '*test.rb' | sort]
Который, в этом случае, заполнит список файлов всеми тестовыми файлами в текущем каталоге, который вы можете обработать, как ожидается:
directorylist.each do |filename|
filename.chomp!
# work with file
end
Автор: cynicalman
Размещён: 05.08.2008 02:08
61 плюса
Вот лучшая на мой взгляд статья о запуске сценариев оболочки в Ruby: « 6 способов запуска команд оболочки в Ruby ».
Если вам нужно только получить выходные данные, используйте обратные метки.
Мне нужны были более продвинутые вещи, такие как STDOUT и STDERR, поэтому я использовал камень Open4. У вас есть все методы, объясненные там.
Автор: Mihai A Размещён: 02.09.2008 11:0534 плюса
Мой любимый это Open3
require "open3"
Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
Автор: anshul
Размещён: 18.09.2008 05:47
24 плюса
При выборе между этими механизмами необходимо учитывать следующие факторы:
- Вы просто хотите использовать стандартный вывод или вам тоже нужен стандартный вывод? или даже выделены?
- Насколько велика ваша продукция? Вы хотите сохранить весь результат в памяти?
- Вы хотите прочитать некоторые из ваших выводов, пока подпроцесс еще работает?
- Вам нужны коды результатов?
- Вам нужен объект ruby, который представляет процесс и позволяет вам убивать его по требованию?
Вам может понадобиться что угодно, от простых обратных галочек (``), system () и IO.popen
до полномасштабных Kernel.fork
/ Kernel.exec
с IO.pipe
и IO.select
.
Вы также можете добавить тайм-ауты в микс, если подпроцесс занимает слишком много времени для выполнения.
К сожалению, это очень многое зависит .
Автор: Nick Brosnahan Размещён: 07.08.2008 05:1021 плюса
Еще один вариант:
Когда ты:
- нужен как 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.
21 плюса
Я определенно не эксперт по Ruby, но я попробую:
$ irb
system "echo Hi"
Hi
=> true
Вы также должны быть в состоянии сделать такие вещи, как:
cmd = 'ls'
system(cmd)
Автор: Steve Willard
Размещён: 05.08.2008 01:24
14 плюса
Ответы выше уже довольно хороши, но я действительно хочу поделиться следующей сводной статьей: « 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>]
Автор: Utensil
Размещён: 07.06.2013 02:07
12 плюса
Если вам действительно нужен 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 должен соблюдать shebang, но вы всегда можете использовать system("bash ./my_script.sh")
его, чтобы убедиться (хотя при /bin/sh
запуске могут быть небольшие накладные расходы /bin/bash
, вы, вероятно, не заметите).
8 плюса
Вы также можете использовать операторы backtick (`), аналогичные Perl:
directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory
Удобно, если вам нужно что-то простое.
Какой метод вы хотите использовать, зависит от того, что именно вы пытаетесь достичь; проверьте документы для более подробной информации о различных методах.
Автор: Rufo Sanchez Размещён: 05.08.2008 01:577 плюса
Самый простой способ, например:
reboot = `init 6`
puts reboot
Автор: Alex Lorsung
Размещён: 30.03.2017 06:13
6 плюса
Используя ответы здесь и связанные в ответе Михая, я собрал функцию, которая отвечает этим требованиям:
- Аккуратно фиксирует 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"
Автор: Ryan Tate
Размещён: 20.02.2012 11:36
6 плюса
Мы можем достичь этого несколькими способами.
Используя Kernel#exec
, ничего после этой команды не выполняется:
exec('ls ~')
С помощью backticks or %x
`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"
Использование Kernel#system
команды возвращает в true
случае успеха, false
если не удалось, и возвращает в nil
случае сбоя выполнения команды:
system('ls ~')
=> true
Автор: nkm
Размещён: 19.02.2012 06:07
6 плюса
Не забудьте 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
но он не ждет завершения команды.
4 плюса
Если у вас есть более сложный случай, чем общий случай (с которым невозможно справиться ``
), тогда посмотрите Kernel.spawn()
здесь . Похоже, это наиболее универсальный / полнофункциональный инструмент, предоставляемый стандартным Ruby для выполнения внешних команд.
Например, вы можете использовать его для:
- создавать группы процессов (Windows)
- перенаправлять ошибки, файлы / друг друга.
- установить env vars, umask
- изменить директорию перед выполнением команды
- установить ограничения ресурсов для процессора / данных / ...
- Делайте все, что можно сделать с другими вариантами в других ответах, но с большим количеством кода.
Официальная рубиновая документация имеет достаточно хороших примеров.
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)
Автор: Kashyap
Размещён: 11.12.2015 02:57
4 плюса
Метод backticks `является самым простым для вызова команд оболочки из ruby. Возвращает результат команды оболочки.
url_request = 'http://google.com' result_of_shell_command = `curl #{url_request}`
3 плюса
Дана команда, например, attrib
require 'open3'
a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
puts stdout.read
end
Я обнаружил, что, хотя этот метод не такой запоминающийся, как, например, system ("thecommand") или команда в backticks, хорошая вещь в этом методе по сравнению с другими методами ... например, 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
Автор: barlop Размещён: 19.12.2017 05:541 плюс
Не совсем ответ, но, возможно, кто-то найдет это полезным, и это касается этого.
При использовании 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.
-1 плюса
Вот классный пример, который я использую в сценарии ruby на OS X (чтобы я мог запустить сценарий и получить обновление даже после переключения из окна):
cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
Автор: JayCrossler
Размещён: 14.10.2014 08:12
Вопросы из категории :
- ruby Вызов команд оболочки из Ruby
- ruby Почему в Ruby нет реального StringBuffer или StringIO?
- ruby Как создать случайный 10-значный номер в рубине?
- ruby Каков наилучший способ конвертировать массив в хеш в Ruby
- ruby Determine file type in Ruby
- ruby Раскладка каталога для чистого Ruby-проекта
- shell Как разрешить символические ссылки в сценарии оболочки
- shell Проверьте, существует ли каталог в сценарии оболочки
- shell Как я могу отправить стандартный вывод одного процесса нескольким процессам, используя (желательно безымянные) каналы в Unix (или Windows)?
- shell Как подавить прекращение сообщения после убийства в bash?
- shell sudo echo "что-то" >> / etc / privilegedFile не работает
- interop Пример кода для использования IBM PCOMM в C # o для написания скребка as400
- interop Как правильно очистить объекты взаимодействия Excel?
- interop Ссылка на движок Google V8 от приложения .NET.
- interop Better way to cast object to int
- interop Вызов C # кода из C ++