| Содержание | Предисловие | Введение | Ссылки
| Глава 1 | Глава 2 | Глава 3 | Глава 4 | Глава 5 | Глава 6 | Глава 7 | Глава 8 | Глава 9 | Глава 10
| Глава 11 | Глава 12 | Глава 13 | Глава 14 | Глава 15 | Глава 16 | Глава 17 | Глава 18 | Глава 19
| Приложение А | Приложение Б | Приложение В | Приложение Г |

Глава 12

    В этой главе:


Доступ к каталогам

Перемещение по дереву каталогов

Вы уже, вероятно, знакомы с понятием "текущий каталог" и с тем, как использовать команду cd shell. В системном программировании для изменения текущего каталога процесса вы производили бы системный вызов chdir. В Perl тоже используется это имя.

Функция chdir в Perl принимает один аргумент выражение; при его вычислении определяется имя каталога, который становится текущим. Как и в большинстве других системных вызовов, при успешном изменении текущего каталога на затребованный chdir возвращает значение "истина", а при неудачном исходе "ложь". Вот пример:

chdir("/etc") || die "cannot cd to /etc ($!)";

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

print "where do you want to go? ";

chomp($where = <STDIN>) ;

if (chdir $where) f

# получилось ) else {

# не получилось >

Вы не сможете определить, где вы находитесь, не запустив команду pwcf. О запуске команд мы расскажем в главе 14.

* Или не использовав функцию getcwd () из модуля Cwd.

Для каждого процесса* назначается свой текущий каталог. Когда запускается новый процесс, он наследует текущий каталог своего родительского процесса, но на этом вся связь заканчивается. Если Perl-программа меняет свой каталог, это не влияет на родительский shell (или иной объект), который запустил этот Perl-процесс. Точно так же процессы, создаваемые Perl-программой, не влияют на текущий каталог самой этой программы. Текущие каталоги для новых процессов наследуются от текущего каталога Perl-программы.

По умолчанию функция chdir без параметра делает текущим начальный каталог, почти так же, как команда cd shell.

Развертывание

Если в качестве аргумента в командной строке стоит звездочка (*), то shell (или тот интерпретатор командной строки, которым вы пользуетесь) интерпретирует ее как список имен всех файлов, находящихся в текущем каталоге. Так, если вы дали команду rm *, то она удалит все файлы из текущего каталога. (Не делайте этого, если не хотите потом долго приставать к системному администратору с просьбами о восстановлении файлов.) Еще примеры: аргумент [а-т] *. с превращается в список имен файлов, находящихся в текущем каталоге, которые начинаются с одной из букв первой половины алфавита и заканчиваются на .с, а аргумент /etc/host* в список имен файлов каталога /etc, которые начинаются со слова host. (Если для вас это ново, то перед дальнейшей работой рекомендуем почитать дополнительную литературу о сценариях shell.)

Преобразование аргументов вроде * или /etc/host* в список соответствующих имен файлов называется развертыванием (globbing). Perl обеспечивает развертывание с помощью очень простого механизма: подлежащий развертыванию образец заключается в угловые скобки или же используется функция glob.

= </etc/host*> = glob("/de/host*");

В списочном контексте, как показано выше, результатом развертывания является список всех имен, которые совпадают с образцом (как если бы shell раскрыл указанные аргументы), или пустой список, если совпадения не обнаружено. В скалярном контексте возвращается следующее совпадающее имя, а если совпадений нет возвращается undef; это очень похоже на чтение из дескриптора файла. Например, просмотр имен по одному реализуется с помощью следующего кода:

while (defined($nextname = </etc/host*>)) ( print "one of the files is $nextname\n";

>

* Это справедливо для UNIX и большинства других современных операционных систем.

Здесь возвращенные имена файлов начинаются с префикса, соответствующего пути доступа к ним (/etc/host), поэтому, если вам нужна только последняя часть имени, придется выделять ее самостоятельно, например:

while ($nextname = </etc/host*>) (

$nextname =~ s#.*/##; # удалить часть до последней косой черты

print "one of the files is $nextname\n";

}

Внутри аргумента допускается указывать несколько образцов; эти списки развертываются отдельно, а затем конкатенируются так, как будто это один большой список:

@fred_barney_files = <fred* barney*>;

Другими словами, эта операция развертывания выдает те же значения, которые возвратила бы эквивалентная ей команда echo с такими же параметрами.*

Хотя развертывание списка файлов и сопоставление с регулярным выражением осуществляются почти одинаково, специальные символы имеют совершенно разный смысл. Не путайте эти механизмы, иначе будете удивляться, почему вдруг <\. с$> не находит все файлы, имена которых заканчиваются на .с!

В аргументе функции glob перед развертыванием производится интерполяция переменных. С помощью Perl-переменных можно выбирать файлы, задаваемые строкой, вычисляемой во время выполнения:

if (-d. "/usr/etc") (

$where = "/usr/etc";

} else (

$where = "/etc";

) Sfiles = <$where/*>;

Здесь переменной $ where присваивается одно из двух имен каталогов в зависимости от того, существует каталог /usr/etc или нет. Затем мы получаем список файлов, находящихся в выбранном каталоге. Обратите внимание:

переменная $where является развертываемой, т.е. подлежащими развертыванию символами являются /etc/* или /usr/etc/*.

Есть одно исключение из этого правила: образец <$var> (который означает использование в качестве развертываемого выражения переменной $var) должен записываться как <${var}>. В причины появления этого исключения сейчас лучше не вдаваться.**

* Для вас не будет сюрпризом узнать о том, что для выполнения развертывания Perl просто запускает C-shell, который раскрывает указанный список аргументов, и производит синтаксический анализ того, что получает обратно.

** Конструкция <$fred> читает строку из дескриптора файла, заданного содержимым скалярной переменной $ fred. Наряду с некоторыми другими особенностями, не упомянутыми в нашей книге, эта конструкция позволяет использовать косвенные дескрипторы файлов, где имя дескриптора передается и обрабатывается так, как будто это данные.

Дескрипторы каталогов

Если в вашей конкретной разновидности операционной системы имеется библиотечная функция readdir или ее функциональный эквивалент, Perl обеспечивает доступ к этой программе (и ее спутницам) с помощью дескрипторов каталогов. Дескриптор каталога это имя из отдельного пространства имен, и предостережения и рекомендации, которые касаются дескрипторов файлов, касаются также и дескрипторов каталогов (нельзя использовать зарезервированные слова, рекомендуется использовать верхний регистр). Дескриптор файла fred и дескриптор каталога fred не связаны между собой.

Дескриптор каталога представляет собой соединение с конкретным каталогом. Вместо чтения данных (как из дескриптора файла) вы используете дескриптор каталога для чтения списка имен файлов в этом каталоге. Дескрипторы каталогов всегда открываются только для чтения; нельзя использовать дескриптор каталога для изменения имени файла или удаления файла.

Если функции readdir() и ее аналогов в библиотеке нет (и при инсталляции языка Perl никакую замену вы не предусмотрели), то использование любой из этих программ приведет к фатальной ошибке и ваша программа компиляцию не пройдет: она аварийно завершится до выполнения первой строки кода. Perl всегда старается изолировать вас от влияния рабочей среды, но такие чудеса ему не подвластны.

Открытие и закрытие дескриптора каталога

Функция opendir работает аналогично библиотечному вызову с тем же именем в С и C++. Ей дается имя нового дескриптора каталога и строковое значение, задающее имя открываемого каталога. Если каталог может быть открыт, opendir возвращает значение "истина"; в противном случае она возвращает "ложь". Вот пример:

opendir(ETC,"/etc") || die "Cannot opendir /etc: $!";

После этого обычно следуют разного рода манипуляции с дескриптором каталога etc, но сначала, наверное, нужно разобраться, как закрывать дескриптор каталога. Это делается с помощью функции closedir, которая весьма похожа на close:

closedir(ETC);

Как и close, closedir часто оказывается ненужной, потому что все дескрипторы каталогов автоматически закрываются перед повторным открытием либо в конце программы.

Чтение дескриптора каталога

Открыв дескриптор каталога, мы можем прочитать список имен с помощью функции readdir, которая принимает единственный параметр дескриптор каталога. Каждый вызов readdir в скалярном контексте возвращает следующее имя файла (только основное имя: в возвращаемом значении никаких косых нет) в порядке, который на первый взгляд кажется случайным*. Если больше имен нет, readdir возвращает undef**. Вызов readdir в списочном контексте возвращает все оставшиеся имена файлов в виде списка с одним именем, приходящимся на каждый элемент. Вот пример, в котором выдается перечень всех имен файлов, содержащихся в каталоге /etc:

opendir(ETC,"/etc") II die "no etc?: $!";

while ($name = readdir(ETC)) f t скалярный контекст, по одному на цикл

print "$name\n"; #выводит ., .., passwd, group и т.д. 1 closedir(ETC) ;

А вот как можно получить все имена в алфавитном порядке с помощью функции sort:

opendir(ETC,"/etc") || die "no etc?: $!";

foreach $name (sort readdir(ETC)) ( # списочный контекст с сортировкой

print "$name\n"; #выводит ., .., passwd, group и т.д. ) closedir(ETC) ;

В этот список включены имена файлов, которые начинаются с точки. Это не похоже на результат развертывания, выполненного с использованием <*>, при котором имена, начинающиеся с точки, не возвращаются. С другой стороны, это похоже на результат работы команды echo* shell.

* Точнее говоря это порядок, в котором имена файлов расположены в каталоге, т.е. тот же "беспорядочный порядок", в котором вы получаете файлы в ОС UNIX в результате вызова команды .find или Is -f.

** Это означает, что при работе с опцией-w вам придется использовать цикл while (defined

($name = readdir (...)).

Упражнения

Ответы см. в приложении А.

1. Напишите программу, которая открывает каталог, заданный ее параметром, а затем выдает список имен файлов этого каталога в алфавитном порядке. (В случае, если переход в каталог не может быть выполнен, программа должна предупредить об этом пользователя.) "

2. Модифицируйте программу так, чтобы она выдавала имена всех файлов, а не только те, имена которых не начинаются с точки. Попробуйте сделать это как с помощью операции развертывания, так и посредством использования дескриптора каталога.




|     Назад     |     Вперед     |


| Содержание | Предисловие | Введение | Ссылки
| Глава 1 | Глава 2 | Глава 3 | Глава 4 | Глава 5 | Глава 6 | Глава 7 | Глава 8 | Глава 9 | Глава 10
| Глава 11 | Глава 12 | Глава 13 | Глава 14 | Глава 15 | Глава 16 | Глава 17 | Глава 18 | Глава 19
| Приложение А | Приложение Б | Приложение В | Приложение Г |