www.web-mastering.ru - ресурс для веб-мастера
реклама на нашем сайте ($5 / месяц)
статей: 208
электронных книг: 8
прислать новую статью/книгу

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

на главную
к началу раздела

урок: предыдущий следующий

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

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

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

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

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

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

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

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

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

foreach $file (<*.o>) { # пройти по списку .о-файлов
unlink($file) || warn "having trouble deleting $file: $!";
}

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

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

foreach (<*.o>) { # пройти по списку .o-файлов
unlink || warn "having trouble deleting $_: $!";
}

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

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

rename("fred","barney") || 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 обозначает barney/wilma.

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

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

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

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

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

ln fred bigdumbguy

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

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

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

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


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

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

ln -s barney neighbor

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

foreach $file ("fred","barney") {
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-м году*.)


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

Функция 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");

Упражнения.

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

  1. Напишите программу, которая работает как утилита rm, удаляя файлы, имена которых были заданы как аргументы командной строки при вызове программы. (Никакие опции команды rm вам для этого не понадобятся.)
  2. Обязательно проверьте эту программу с почти пустым каталогом, чтобы случайно не удалить нужные файлы! Помните, что аргументы командной строки при запуске программы извлекаются из массива @argv.
  3. Напишите программу, которая работает как утилита mv, переименовывая первый аргумент командной строки во второй аргумент. (Никакие опции команды mv вам для этого не нужны, и аргументов нужно всего два.) Можете также рассмотреть случай переименования, когда целевым объектом является каталог.
  4. Напишите программу, которая работает, как ln, создавая жесткую ссылку из первого аргумента командной строки на второй аргумент. (Никакие опции команды ln вам для этого не нужны, и аргументов нужно всего два.)
  5. Если у вас есть символические ссылки, модифицируйте программу из предыдущего упражнения так, чтобы она работала с необязательным ключом -s.
  6. Если у вас есть символические ссылки, напишите программу, которая ищет в текущем каталоге все файлы, на которые есть такие ссылки, и выводит их имена и значения ссылок так, как это делает ls -1 (имя -> значение). Создайте в текущем каталоге несколько символических ссылок и проверьте программу.

 

Copyright © 2004 www.web-mastering.ru