Программирование на языке Ruby - Хэл Фултон
Шрифт:
Интервал:
Закладка:
Если методу перенаправления передать в качестве параметра строку, то она будет считаться именем файла. Если же параметром является объект 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"
puts sh.pwd # /tmp
sh.popd
puts sh.pwd # /home
Для удобства в класс Shell импортируются методы из различных источников, в том числе из класса File, модуля FileTest и библиотеки ftools.rb. Это избавляет от необходимости выполнять require, include, создавать объекты, квалифицировать вызовы методов и т. д.
sh = Shell.new
flag1 = sh.exist? "myfile" # Проверить существование файла.
sh.delete "somefile" # Удалить файл.
sh.move "/tmp/foo", "/tmp/bar" # Переместить файл.
У библиотеки Shell есть и другие возможности, которые мы здесь не рассматриваем. Дополнительную информацию ищите в документации.
14.4. Переменные окружения
Иногда необходимо обращаться к переменным окружения, которые являются связующим звеном между программой и внешним миром. Переменные окружения — это просто метки, связанные с некоторым текстом (обычно небольшим); в них хранятся, например, пути к файлам, имена пользователей и т.п.
Переменные окружения широко применяются в ОС UNIX. Система Windows (а еще раньше MS-DOS) позаимствовала эту идею у UNIX, поэтому приведенные ниже коды будут работать на обеих платформах.
14.4.1. Чтение и установка переменных окружения
Глобальная константа ENV — это хэш, с помощью которого можно читать и изменять переменные окружения. В примере ниже мы читаем значение переменной PATH, (в Windows вместо двоеточия нужно употреблять точку с запятой):
bypath = ENV["PATH"]
# А теперь получим массив...
dirs = mypath.split(":")
А вот пример установки переменной. Новый процесс мы создали, чтобы проиллюстрировать две вещи. Во-первых, дочерний процесс наследует переменные окружения от своего родителя. Во-вторых, значение переменной окружения, установленное в дочернем процессе, родителю не видно.
ENV["alpha"] = "123"
ENV["beta"] = "456"
puts "Родитель: alpha = #{env['alpha']}"
puts "Родитель: beta = #(env['beta']}"
fork do # Код потомка...
x = ENV["alpha"]
ENV["beta"] = "789"
y = ENV["beta"]
puts " Потомок: alpha = #{x}"
puts " Потомок: beta = #{y}"
end
Process.wait
a = ENV["alpha"]
b = ENV["beta"]
puts "Родитель: alpha = #{a}"
puts "Родитель: beta = #{b}"
Программа выводит следующие строки:
Родитель: alpha = 123
Родитель: beta = 456
Потомок: alpha = 123
Потомок: beta = 789
Родитель: alpha = 123
Родитель: beta = 456
Это следствие того факта, что родитель ничего не знает о переменных окружения своих потомков. Поскольку программа на Ruby обычно исполняется в подоболочке, то после ее завершения все сделанные изменения переменных окружения не будут видны в текущей оболочке.
14.4.2. Хранение переменных окружения в виде массива или хэша
Важно понимать, что объект ENV — не настоящий хэш, а лишь выглядит как таковой. Например, мы не можем вызвать для него метод invert; будет возбуждено исключение NameError, поскольку такого метода не существует. Причина такой реализации в том, что существует тесная связь между объектом ENV и операционной системой; любое изменение хранящихся в нем значений отражается на состоянии ОС, а такое поведение с помощью простого хэша не смоделируешь.
Однако имеется метод to_hash, который вернет настоящий хэш, отражающим текущее состояние:
envhash = ENV.to_hash
val2var = envhash.invert
Получив такой хэш, мы можем преобразовать его к любому другому виду (например, в массив):
envarr = ENV.to_hash.to_a
Обратное присваивание объекту ENV недопустимо, но при необходимости можно пойти обходным путем:
envhash = env.to_hash
# Выполняем произвольные операции... и записываем обратно в ENV.
envhash.each {|k,v| ENV[k] = v }
14.4.3. Импорт переменных окружения как глобальных переменных
Существует библиотечка importenv.rb, которая импортирует все переменные окружения, сопоставляя им глобальные переменные программы:
require "importenv"
# Теперь переменные окружения стали глобальными переменными...
# Например, $PWD и $LOGNAME
where = $PWD
who = $LOGNAME
puts "В каталоге #{where}, вошел как #{who}"
Поскольку библиотека importenv пользуется библиотекой trace_var, отражение на самом деле двустороннее: если присвоить глобальной переменной новое значение, реальная переменная окружения получит то же значение.
require "importenv"
puts "Мой путь #$PATH"
# Печатается: /usr/local/bin:/usr/bin:/usr/ucb:/etc:.
$PATH = "/ruby-1.8.0:" + $PATH
puts "Моя переменная $PATH теперь равна #{ENV['PATH']}"
# Печатается: /ruby-1.8.0:/usr/local/bin:/usr/bin:/usr/ucb:/etc:.
Еще раз подчеркнем, что любые изменения переменных окружения, выполненные внутри программы на Ruby, не отражаются на их значениях, видимых вне этой программы.
14.5. Сценарии на платформе Microsoft Windows
Как и на лыжном курорте, где девушки ищут мужей, а мужья — девушек, ситуация не так симметрична, как может показаться на первый взгляд.
Алан Линдсей МаккейУже отмечалось, что Ruby больше любит ОС UNIX. В каком-то смысле это правда: язык разрабатывался в среде UNIX, в ней лучше всего и работает. Сейчас он, впрочем, перенесен на другие платформы, в том числе на Macintosh; ведется даже работа по переносу на Palm OS. Но если UNIX — основная платформа, то следующая по значимости — Windows.