Это послужило основанием для создания библиотеки Shell
, которая упрощает, к примеру, организацию конвейеров команд и перенаправление вывода в файл. Кроме того, она сводит воедино функциональность из разных источников, скрывая ее за интерфейсом объекта Shell
. (На платформе Windows эта библиотека работает не во всех случаях.)
14.3.1. Использование библиотеки Shell для перенаправления ввода/вывода
В классе Shell
для создания объектов есть два метода: new
и cd
. Первый создает объект, ассоциированный с текущим каталогом, второй — объект, для которого рабочим будет указанный каталог.
require 'shell'
sh1 = Shell.new # Работать в текущем каталоге.
sh2 = Shell.cd('/tmp/hal') # Работать в каталоге /tmp/hal.
Библиотека Shell
определяет несколько встроенных команд (например, echo
, cat
и tee
) в виде методов. Они всегда возвращают объекты класса Filter
(как и определяемые пользователем команды, с которыми мы вскоре познакомимся).
Класс Filter
понимает, что такое перенаправление ввода/вывода. В нем определены методы (или операторы) <
, >
и |
, которые ведут себя примерно так, как мы ожидаем по многолетнему опыту написания shell-сценариев.
Если методу перенаправления передать в качестве параметра строку, то она будет считаться именем файла. Если же параметром является объект IO
, он используется для операций ввода/вывода. Примеры:
sh = Shell.new
# Вывести файл motd на stdout.
sh.cat('/etc/motd') > STDOUT
# Напечатать его еще раз.
(sh.cat < '/etc/motd') > STDOUT
(sh.echo 'Это тест') > 'myfile.txt'
# Добавить строку в конец файла /etc/motd.
sh.echo('Hello, world!') >> '/etc/motd'
# Вывести два файла на stdout и продублировать (tee) вывод в третий файл.
(sh.cat 'file1' 'file2') | (tee 'file3') > STDOUT
Отметим, что у оператора >
высокий приоритет. Скобки, которые вы видите в данном примере, в большинстве случаев обязательны. Вот два примера правильного использования и один — неправильного:
# Интерпретатор Ruby понимает такую конструкцию...
sh.cat('myfile.txt') > STDOUT
# ...и такую тоже.
(sh.cat 'myfile.txt') > STDOUT
# TypeError! (ошибка связана с приоритетами).
sh.cat 'myfile.txt' > STDOUT
Отметим еще, что можно «инсталлировать» системные команды по своему выбору. Для этого служит метод def_system_command
. Ниже определяются два метода: ls
и ll
, которые выводят список файлов в текущем каталоге (в коротком и длинном формате).
# Имя метода совпадает с именем команды...
# Необходим только один параметр:
Shell.def_system_command 'ls'
# А здесь должно быть два параметра:
Shell.def_system_command 'll', 'ls -l'
sh = Shell.new
sh.ls > STDOUT # Короткий формат.
sh.ll > STDOUT # Длинный формат.
Вы, наверное, обратили внимание на то, что в большинстве случаев мы явно отправляем вывод объекту STDOUT
. Связано это с тем, что объект Shell
автоматически вывод команд никуда не направляет. Он просто ассоциирует его с объектом Filter
, который уже может быть связан с файлом или с объектом IO
.
14.3.2. Дополнительные замечания по поводу библиотеки shell.rb
Метод transact
исполняет блок в контексте вызывающего объекта. Таким образом, допустима следующая сокращенная запись:
sh = Shell.new
sh.transact do
echo('Строка данных') > 'somefile.txt'
cat('somefile.txt','otherfile.txt') > 'thirdfile'
cat('thirdfile') | tee('file4') > STDOUT
end
Итератор foreach
принимает в качестве параметра файл или каталог. Если это файл, он перебирает все его строки, а если каталог — все имена файлов в нем.
sh = Shell.new
# Напечатать все строки файла /tmp/foo.
sh.foreach('/tmp/foo') {|l| puts l }
# Вывести список файлов в каталоге /tmp.
sh.foreach('/tmp') {|f| puts f }
Метод pushdir
запоминает текущий каталог, а метод popdir
делает последний запомненный каталог текущим. У них есть синонимы pushd
и popd
. Метод pwd
возвращает текущий рабочий каталог, его синонимы — getwd
, cwd
и dir
.
sh = Shell.cd '/home'
puts sh.pwd # /home
sh.pushd '/tmp'