Создание серверных приложений на языке PERL

Манипулирование файлами и каталогами


В зтой главе мы покажем, как можно манипулировать самими файлами, а не только содержащимися в них данными. При демонстрации процедуры доступа к файлам и каталогам мы будем пользоваться семантикой UNIX (a также POSIX и Linux). Есть и другие механизмы доступа к файловим системам, но описываемые здесь средства являются стандартними для современных файлових систем.

Удаление файла

Вы уже научились создавать в Per! файл, открывая его для вывода через дескриптор файла. Сейчас ми освоим опасную процедуру удаления файлов (очень кстати для тринадцатой глави, не правда ли?).

Perl-функция unlink (названная по имени системного вызова POSIX) удаляет одно из имен файла (которнй может иметь и другие имена). Когда удаляется последнее имя файла и ни в одном процессе он не открыт, удаляется и сам файл. Зто в точности соответствует тому, что делает UNIX-команда гт. Поскольку у файла часто бывает только одно имя (если ви не создавали жесткие сснлки), то в большинстве случаев удаление имени можно считать удалением файла. Приняв зто допущенне, покажем, как удалить файл fred, а затем удалить файл, имя которого вводится во время выполнения программы:

unlink ("fred"); # распрощаемся с файлом fred print "what file do you want to delete? ";

chomp($name = <STDIN>) ;

unlink ($name) ;

Функция unlink может принимать также список имен, подлежащих удалению:

unlink ("cowbird","starling"); # убьем двух зайцев unlink <*.о>; # как "rm *.o" в shell

Операция <*. o> выполняется в списочном контексте и создает список имен файлов, которые совпадают с образцом. Зто именно то, что нужно передать в unlink.

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




foreach $file (<*.o>) ( # пройти по списку .о-файлов

unlink($file) || warn "having trouble deleting $file: $!";

1

Если unlink возвращает 1 ( зто означает, что единственный указанный файл был удален), то функция warn пропускается. Если имя файла не может быть удалено, результат "О" означает "ложь", позтому warn выполняется. Абстрактно зто можно прочитать так: "удали зтот файл или сообщи мне о нем".

Если функция unlink дается без аргументов, то по умолчанию вновь используетея переменная $_. Так, приведеними выше цикл можно записать следующим образом:

foreach (<*.о>) ( # пройти по списку .о-файлов

unlink || warn "having trouble deleting $_: $!";



Переименование файла



В shell UNIX имя файла изменяется с помощью команды mv. В Perl зта операция обозначается как rename {$старое,$новое). Вот как следует переименовать файл fred в barney:

rename("fred","barney") II die "Can't rename fred to barney: $!";

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

Когда пользователь вводит mv файл какой-то_каталог, команда /иуделает пару закулисных фокусов и создает полное путевое имя (или, другими словами, полное описание пуги к месту размещения файла). Функция rename зтого делать не умеет. В Perl зквивалентная операция выглядит так:

rename("файл","какой-то_каталог/файл");

Обратите внимание: в Perl нужно указать имя файла в новом каталоге явно. Кроме того, команда mv копирует файл, когда он переименовывается, с одного смонтированного устройства на другое (если вы используете одну из достаточно совершенных операционных систем). Функция rename не настолько умна, позтому вы получите сообщение об ошибке, означающее, что зту проблему нужно решать каким-то иным способом (возможно, посредством вызова команды mv c теми же именами). Модуль File::Find поддерживает функцию move.





Создание для файла альтернативных имен: связывание ссылками



Иногда пользователю нужно, чтобы у файла было два, три, а то и дюжина имен (как будто одного имени файлу не хватает!). Операция присвоєння файлу альтернативних имен называется создание ссылок. Две основних формы создания ссылок — ато создание жестких ссылок и создание симво-лических (или мягких) ссылок. Не все виды файлових систем подцерживают оба типа сеилок или хотя би один из них. В зтом разделе описани файловие системи, соответствующие стандарту POSIX.



Жесткие и символические ссылки



Жесткая ссылка на файл неотличима от его исходного имени; ни одна из жестких ссилок не является более "реальним именем" для файла, чем любая другая.

Операционная система следит за тем, сколько жестких ссылок обознача-ют файл в кажднй данний момент времени. Когда файл впервые создается, у него имеется одна ссылка. Каждая новая жесткая ссылка увеличивает зто число, а каждая удаленная — уменьшает. Когда исчезает последняя ссылка на файл и файл закрнвается, он прекращает своє существование.

Каждая жесткая ссылка на файл должна располагаться в той же файловой системе (обычно зто диск или часть диска). По зтой причино нельзя создать новую жесткую ссилку на файл, находящийся в другой файловой системе.

В большинстве систем применение жестких ссилок для каталогов огра-ничено. Чтобы структура каталогов имела вид дерева, а не произвольную форму, для каталога допускается наличие только одного имени от корня, ссылки из "точечного" файла на данный каталог, и семейства жестких ссылок "точка-точка" из каждого из его подкаталогов. Если ви попитаєтесь создать еще одну жесткую ссылку на каталог, то получите сообщение об ошибке (если только вы не привилегированный пользователь — тогда вам придется провести всю ночь за восстановлением своей поврежденной фай-ловой системи).

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



Например, если символическая ссылка fred содержит имя barney, то указание открыть файл fred — зто, на самом деле, указание открыть файл barney. Если barney — каталог, то fred/wilma обозначает Ьатеу/wilma.

Содержимое символической ссылки (i.e. имя, на которое указывает символическая ссылка) не обязательно должно обозначать существующий файл или каталог. В момент, когда создаетсяу/'erf, существование barney вовсе не обязательно. Более того, Ьатеу может никогда и не появиться! Содержимое символической ссылки может указывать на путь, который ведет за пределы текущей файловой системи, позтому можно создавать символиче-скую ссылку на файл, находящийся в другой смонтированной файловой системо.

Отслеживая новое имя, ядро может натолкнуться на другую символиче-скую ссылку. Зта новая ссылка содержит новые злементы отслеживаемого пуги. Одни символические ссылки могут указывать на другие символические ссылки. Как правило, допускается до восьми уровней символических ссылок, но на практико такое встречается редко.

Жесткая ссылка защищает содержимое файла от уничтожения (потому что она считается одним из имен файла). Символическая же ссылка не может уберечь содержимое файла от исчезновения. Символическая ссылка может указывать на другие смонтированные файловые системи, а жесткая — не может. Для каталога может бить создана только символическая ссылка.



Создание жесткиж и символических ссылок в Perl



В ОС UNIX жесткие ссылки создают с помощью команди In. Например, команда

In fred bigdumbguy

позволяет создать жесткую ссилку из файла fred (которнй должен существо-вать) на bigdumbguy. В Per! зто виражается так:

link("fred","bigdumbguy") || die "cannot link fred to bigdumbguy";

Функция link принимает два параметра — старое имя файла и новий псевдоним для зтого файла. Если ссылка создана успешно, link возвращает значение "истина". Как и команда mv, UNIX-команда In позволяет указывать в качестве псевдонима только каталог (без имени файла). Функция link (как и функция rename) не настолько умна, позтому вы должны указывать полное имя файла явно.



При создании жесткой ссылки старое имя не может быть именем каталога*, а новый псевдоним должен указывать на ту же файловую систему. (Зти ограничения частично обусловили необходимость создания символи-ческих ссылок.)

В системах, которые поддерживают символические ссылки, в команде In может использоваться опция -s, которая создает символическую ссылку. Например, если необходимо создать символическую ссылку из barney на neighbor (чтобы обращение к neighbor фактичесїзі было обращением к barney), следует использовать команду

In -s barney neighbor

В Perl для зтого применяется функция symlink:

symlinkf"barney","neighbor") || die "cannot symlink to neighbor";

Отметим, что barney не обязательно должен существовать — ни сейчас, ни в будущем. В этом случае обращение к neighbor возвратит нечто туманное вроде No such file or directory.

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

if (defined($х = readlink("neighbor"))) ( print "neighbor points at '$x'\n";

вы получите сведения о barney, если все нормально. Если выбранная символическая ссылка не существует, не может быть прочитана или вообще не является таковой, readlink возвращает undef (т.е. вданном случае "ложь") — именно позтому мы ее здесь и проверяем.

В системах, не поддерживающих символические ссылки, обе функции — и symlink, и readlink — потерпят неудачу и выдадут сообщения об ошибке. Perl может "спрятать" от вас некоторые зависящие от конкретной системи особенности, но некоторые все равно проявляются. Зто как раз одна из них.

* Если только вы не привелигированный пользователь и не любите забавляться с командой fsck, восстановливая поврежденную файловую систему.





Создание и удаление каталогов



Вы не смогли бы вьшолнить указанные операции (во всяком случае, в UNIX-системе), не зная о команде mkdir(\), которая создает каталоги, содержащие файлы и другие каталоги. В Perl єсть зквивалент зтой коман-ды — функция mkdir, которая в качестве аргументов принимает имя нового каталога и некое число, определяющее права доступа к созданному каталогу. Права доступа задаются как число, интерпретируемое во внутреннем формате прав доступа. Если вы не знакомы с внутренним форматом прав доступа, обратитесь к man-странице chmod(2). Если вам некогда с зтим разбираться, просто укажите права доступа как 0777, и все будет нормально*. Вот пример создания каталога с именем gravelpit:

mkdir ("gravelpit",0777) || die "cannot mkdir gravelpit: $!";

UNIX-команда rmdir(l) удаляет пустые каталоги. В Perl єсть ее зквивалент с тем же именем. Вот как можно сделать Фреда безработным:

rmdir ("gravelpit") I| die "cannot rmdir gravelpit: $!";

Хотя зти Perl-операции используют преимущества системных вызовов с такими же именами, они будут вьшолняться (хотя и чуточку медленнее) даже в системах, не поддерживающих такие вызовы. Perl вызывает утилиты mkdir и rmdir (или как там они называются у вас в системо) автоматически.



Изменение прав доступа



Права доступа к файлу или каталогу определяют, кто (в широком смысле слова) и что может делать с зтим файлом или каталогом. В UNIX общепри-нятый метод изменения прав доступа к файлу — применение команды chmod(l). (Если вы не знакомы с зтой операцией, обратитесь к ее man-странице.) В Perl права доступа изменяются с помощью функции chmod. Зта функция получает в качестве аргументов заданный восьмеричним числом режим доступа и список имен фаилов и пытается изменить права доступа ко всем зтим файлам в соответствии с указанным режимом. Чтобы сделать файлы fred и Ьатеу доступными в режимах чтения и записи для всех пользователей, нужно вьшолнить такую операцию:

chmod(0666,"fred","barney");



Режим 0666 обозначает чтение и запись для владельца, группы и прочих пользователей, т.е. как раз то, что нам нужно.

* В данном случае вы не создаете каталог с самими широкими правами доступа. Определить права доступа вам также поможет текущая маска доступа umask вашего процесса. В UNIX-системах см. описание комавды shell umask или man-страницу umask(2).

Функция chmod возвращает число файлов, для которых были успешно изменены права доступа (даже если в результате фактически ничего не изменилось). Таким образом, в отношении контроля ошибок она работает аналогично функции unlink. Позтому, чтобы изменить права доступа к файлам fred и Ьагпеу и выполнить контроль ошибок в каждом случае, необходимо использовать следующую конструкцию:

foreach $file ("fred","barney") f unless chmod (0666,$file) (

warn "hmm... couldn't chmod $file.\$!";



Изменение принадлежности



Каждый файл в файловой системе (обычный, каталог, файл устройства и т.д.) имеет владельца и группу. Зги параметры определяют, кому принадлежат права доступа, установленные для файла по категориям "владелец" и "группа" (чтение, запись и (или) выполнение). Владелец и группа определяются в момент создания файла, но при определенных обстоятельствах вы можете изменить их. (Зти обстоятельства зависят от конкретной разновидности UNIX, c которой вы работаете; подробности см. на man-странице chown.)

Функция chown получает идентификатор пользователя (UID), иденти-фикатор группы (GID) и список имен файлов и пытается изменить принад-лежность каждого из перечисленных файлов в соответствии с указанными идентификаторами. Успешному результату соответствует ненулевое значе-ние, равное числу файлов, принадлежность которых изменена (как в функ-циях chmod и unlink). Обратите внимание: вы одновременно меняете и владельца, и группу. Если какой-то из зтих идентификаторов вы менять не хотите, поставьте вместо него -1. Помните, что нужно использовать числовые UID и GID, а не соответствующие символические имена (хотя команда chmod и принимает такие имена). Например, если UID файла fred — 1234, а идентификатор группы stoners, которой зтот файл принадлежит по умолчанию,— 35, то в результате применения следующей команды файлы slate и granite переходят к пользователю fred и его группе:



chown(1234, 35, "slate", "granite"); # то же, что й

# chown fred slate granite

# chgrp stoners slate granite



В главе 16 вы узнаете, как преобразовать

fred в 1234 и stoners в 35.



Изменение меток времени



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

зто время последнего доступа, время последнего изменения и время послед-него изменения индексного дескриптора. Первым двум моткам времени можно присвоить произвольные значення с помощью функции utime (ко-торая соответствует системному вызову utime в ОС UNIX). При установке двух зтих значений третья метка автоматически устанавливается в текущее время, позтому отдельного способа для ее установки не предусмотрено.

Зти значення устанавливаются во внутреннем формате времени, а имен-но в количестве секунд, прошедших после полуночи 1 января 1970 года по среднегринвичскому времени. Когда мы писали нашу книгу, зта цифра достигла 800 миллионов с небольшим. (Во внутреннем формате она пред-ставляется как 32-разрядное число без знака, и если все мы не перейдем на 64-разрядные (й более) машины, то переполнение наступит где-то в следую-щем столетии. У нас будут гораздо более серьезные проблеми в 2000-м году*.)

Функция utime работает аналогично функциям chmod и unlink. Она получает список имен файлов и возвращает число файлов, параметри времени которых были изменены. Вот что нужно сделать, чтобы файли fred и barney выглядели так, будто они изменялись в недавнем прошлом:

$atime = $mtime = 700_000_000; # некоторое время назад utime($atime,$mtime,"fred","barney")

Никакого "розумного" значення для меток времени нет: можно сделать так, чтобы файл выглядел сколь угодно старым, или чтобы казалось, будто он бил изменен в далеком будущем (зто полезно, если вы пишете научно-фантастические рассказы). Вот как, например с помощью функции time (которая возвращает текущее время как метку времени UNIX) можно сделать так, чтобы казалось, будто файл max_headroom был изменен спустя 20 минут после текущего момента времени:



$when = time() + 20*60; # 20 минут с текущего момента utime($when,$when,"max headroom");

* Perl- функции localtime nqmtime работаюттак, каквС: они возвращают год, изкоторого вычтена цифра 1900. В 2003-м году localtime выдаст год как 103.



Упражнения



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

Напишите программу, которая работает как утилита гт, удаляя файлы, имена которых были заданы как аргументи командной строки при вызове программы. (Никакие опции команды ті вам для зтого не понадобятся.)

Обязательно проверьте зту программу с почти пустым каталогом, чтобы случайно не удалить нужные файлы! Помните, что аргументы командной строки при запуске программы извлекаются из массива @argv.

Напишите программу, которая работает как утилита mv, переименовывая первый аргумент командной строки во второй аргумент. (Никакие опции команды mv вам для зтого не нужны, и аргументов нужно всего два.) Можете также рассмотреть случай переименования, когда целевым обь-ектом является каталог.

Напишите программу, которая работает, как In, создавая жесткую ссьиіку из первого аргумента командной строки на второй аргумент. (Никакие опции команды In вам для зтого не нужны, и аргументов нужно всего два.)

Если у вас єсть символические ссылки, модифицируйте программу из предыдущего упражнения так, чтобы она работала с необязательным ключом -s.

Если у вас єсть символические ссылки, напишите программу, которая ищет в текущем каталоге все файлы, на которые єсть такие ссылки, и выводит их имена и значення ссылок так, как зто делает Is -1 (имя -> значение). Создайте в текущем каталоге несколько символических ссы-лок и проверьте программу.


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



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


Содержание раздела