Основы Perl — ввод/вывод, файлы, каталоги
Ввод и вывод
Как всегда, давайте начнем с примера:
use strict;while(my $line = <STDIN>) {
chomp($line); # отсекаем символ \n
last if($line eq «exit»);
print «Вы ввели ‘$line’. Для выхода из цикла введите exit\n»;
}
Рассмотрим четвертую строку. STDIN — это файловый дескриптор, связанный со стандартным потоком ввода. Чтение из файла производится с помощью оператора «треугольные скобки». Да, это не так привычно, как readln, но такая запись тоже имеет право на жизнь, тем более, что она короче.
Функция chomp проверяет, находится ли в конце строки-аргумента символ новой строки (\n) и если он присутствует, отсекает его. Если такого символа нет, строка остается без изменений.
Функция print
нам уже хорошо знакома — она выводит строку-аргумент в стандартный поток вывода. Однако что нам не известно об этой функции, так это то, что существует специальная форма записи, позволяющая явно задать файловый дескриптор, в который происходит запись:
print STDERR «Вывод строки в стандартный поток ошибок\n»;
Обратите внимание, что после файлового дескриптора не ставится запятая. Вопрос «почему это так» — выходит за рамки статьи. Можете считать, что это просто исключение из правил.
Приведенный выше скрипт можно немного «ужать» за счет использования переменной $_. Напоминаю, что впервые мы столкнулись с ней во втором уроке.
use strict;while(<STDIN>) { # результат чтения сохраняется в переменной $_
chomp; # отсекаем символ \n в переменной $_
last if($_ eq «exit»);
printf «Вы ввели ‘%s’. Для выхода из цикла введите exit\n», $_;
}
Заметьте также, что функция print
была заменена на printf
, которая должна быть хорошо знакома программистам на Си. Я полагаю, что вы уже знакомы с форматированным выводом. Если это не так — ничего страшного, всю необходимую информацию можно получить, выполнив команды perldoc -fprintf
и perldoc -f sprintf
.
Файлы — чтение и запись
Чтение и запись в случае с файлами производится точно так же, как чтение из STDIN и запись в STDOUT или STDERR. Единственное, о чем следуетпозаботится — это создать дескриптор, связанный с заданным файлом.
use strict;
# создаем файловый дескриптор
open FID, «input.txt»
or die «Failed to open input.txt: $!\n»;
my $i = 0;
print «line «.++$i.»: $_» while(<FID>);
close FID; # закрываем файловый дескриптор
Собственно создание файлового дескриптора происходит на четвертой строчке. Функция open принимает два аргумента — дескриптор и имя файла. В случае ошибки, например, если файла с именем input.txt не существует, функция вернет false, а сообщение об ошибке будет записано в переменную $!
.
Но что делает оператор «или» после вызова open? Конструкция вида «функция1 or функция2» встречается в perl-скриптах довольно часто, так что давайте разберемся, как она работает.
Будем считать, что функция1 и функция2 возвращают ложь в случае ошибки и истину иначе. Сначала выполняется функция1. Если она завершается успешно, будет возвращено истинное значение и интерпретатору Perl следует вычислить «истина или (что-то, что вернет функция2)». Но «истина или ложь» = «истина или истина» = «истина». Другими словами, в этом случае результат не зависит от того, что вернет функция2, а значит не следует тратить время на ее выполнение.
Если же функция1 завершится с ошибкой и вернет «ложь», то результат «ложь или функция2» напрямую зависит от того, что вернет функция2, значит в этом случае интерпретатор будет обязан ее вызвать. Таким образом конструкция «функция1 or функция2» означает «вызвать функцию1 и если она вернет ложь (и только в этом случае!) вызвать функцию2».
Значит, если вызов open
завершится неудачно, будет вызвана функция die
, которая делает что? Как несложно догадаться по ее названию — выводит сообщение об ошибке и завершает работу скрипта. Остальная часть примера настолько тривиальна, что я не вижу смысла ее описывать.
Только что мы рассмотрели открытие файла на чтение. Чтобы открыть файл на запись, нужно перед его именем указать > или >>:
use strict;
# пример 1
open FID, «>output1.txt»; # один знак «больше»
or die «Failed to open output1.txt: $!\n»;
print FID «111\n»; # обратите внимание — запятой нет
close FID;# пример 2
open FID, «>>output2.txt»; # два знака «больше»
or die «Failed to open output2.txt: $!\n»;
print FID «222\n»;
close FID;
В первом случае, если файл output1.txt уже существует, он будет очищен, иначе — файл output1.txt будет создан. Во втором случае, если файл output2.txt уже существует, запись будет производится в его конец, иначе также будет создан новый файл. Чтобы почувствовать разницу, можете несколько раз запустить этот скрипт и посмотреть, что будет записано в каждом из файлов.
Здесь мы рассмотрели лишь самые распространенные способы вызова функцииopen
. Вывод команды perdoc -f open
занимает 19 килобайт. Это не случайно, ведь функцию open можно вызывать с тремя аргументами, с ее помощью можно сохранять файловые дескрипторы в скалярных переменных и даже перенаправлять данные в уже открытых дескрипторах. Разумеется, в рамках одной заметки я не могу рассказать о всех возможностях этой функции (более того — скорее всего я их всех и не знаю), так что можете считать чтение соответствующей документации домашним заданием.
В заключение к этому разделу хотелось бы рассказать об интересной особенности оператора <>. Дело в том, что он может использоваться без указания файлового дескриптора:
print while(<>); # аналогично print $_ while($_ = <>);
В этом случае, если скрипт был вызван без аргументов, оператор будет производить чтение из STDIN. В противном случае все аргументы будут рассматриваться, как имена файлов. Чтение будет производиться по очереди из каждого файла. Можно сказать, что этот скрипт представляет собой аналог утилиты cat.
Работа с каталогами
Самый простой способ получить имена всего, что есть в каталоге — это воспользоваться функциями opendir
и readdir
:
use strict;
opendir DIR, «/home/afiskon» or die $!;
while(my $fname = readdir DIR) {
print «$fname\n»;
}
closedir DIR;
Тут все настолько просто, что даже комментировать не хочется. Давайте лучше сразу перейдем к более интересному примеру:
use strict;my @flist = glob «~/*.txt»;
print «$_\n» for(@flist);
Функция glob возвращает список файлов, как это сделала бы оболочка csh. Тильда означает домашний каталог, звездочка — любую последовательность символов. Также можно использовать знак вопроса, фигурные и квадратные скобки.
Существует и более короткая запись:
А еще можно так:
Использовать функцию glob лучше в тех случаях, когда в выражение нужно подставить переменную.
Другие операции над файлами и каталогами
Эта заметка была бы не полной, если бы я не рассказал, как в Perl создать/удалить каталог или определить размер файла. Но не беспокойтесь, этот раздел очень простой. Фактически сейчас моя задача — привести список функций и операторов, чтобы в случае необходимости вы знали, в какой мануал заглянуть (вы ведь уже запомнили команду «perldoc -f имя_функции», верно?) :
# функция возвращает число успешно удаленных файлов
unlink $file1, $file2, $file3;# а еще можно так:
unlink glob «~/*.core»;
# переименовать или переместить файл
rename $old, $new;
# создать каталог, второй аргумент представляет
# собой права доступа и является необязательным
mkdir $dir_name, 0755;
# удалить пустой каталог
rmdir dir;
# сменить права доступа к файлу или файлам
chmod 0755, $file1, $file2, $file3;
# сменить владельцев файлов
chown $user, $group, glob «*.txt»
# по моему опыту работать с ссылками приходится не часто
# но на всякий случай вот список имен функций:
# link, symlink, readlink
Также в Perl имеется несколько довольно полезных операторов:
if(-f $fname) { # является файлом
my $fsize = -s $fname; # получить размер файла
# сделать с ним еще что-то
} elsif(-d $fname) { # является каталогом
my $readable = -r $fname; # доступен на чтение?
# ….
} else {
die «EPIC FAIL\n»;
}
}
С непривычки эти операторы могут показаться странными, но на самом деле они мало чем отличаются от банальных «больше» и «меньше». Полный список операторов этого типа можно посмотреть, выполнив команду perldoc -f -X
.