Как переименовать файл в perl

Обновлено: 04.07.2024

Как-то я столкнулся с такой ситуацией: у меня был каталог с HTML-файлами в кодировке Windows-1251, имена которых (index.htm, index(1).htm, index(2). htm. ) ничего не говорили об их содержимом.

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

Файлов было много, и переименовывать их вручную, конечно, не хотелось, поэтому я написал простую программу на Perl (ActivePerl 5.8.6.811 для Windows). При ее запуске в командной строке указывался каталог для обработки, и программа переименовывала в нем все файлы с расширением htm в соответствии с их заголовками.

Таким образом, поставленная задача была решена. Но я не остановился на этом и продолжил расширение возможностей, вынеся тело подпрограммы get_title в отдельный файл (подключаемый модуль). Изменив эту подпрограмму и указав ее имя при запуске, можно расширить функциональность — обрабатывать с ее помощью не только HTML-файлы, но и другие.

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

Само собой разумеется, что после таких нововведений расширение имени файлов, обрабатываемых программой, уже не могло быть ограничено одним htm, ведь появилась возможность обработки и других типов (да и HTML-файлы могут быть названы по-другому: html, phtml, shtml. ). Поэтому я предусмотрел указание одного или нескольких расширений в командной строке, а для обработки файлов с любыми расширениями можно задать символ «звездочка» («*»).

Иногда бывает нужно обработать файлы, находящиеся не только в указанном каталоге, но и во всех его подкаталогах (рекурсивно), т. е. обработать сразу целую ветвь дерева каталогов. А порой требуется обработать лишь один конкретный файл. Эти функции также были добавлены. Кроме того, была предусмотрена отмена переименования файлов, для чего в каждом каталоге, где хотя бы один файл был переименован, создается специальный perl-скрипт, который, будучи запущен пользователем, выполнит обратное переименование файлов (вернет им прежние имена). Такая возможность повышает безопасность использования программы (если вдруг вы, скажем, переименуете не те файлы, то потом сумеете вернуть все обратно). Полезна она и при отладке собственных подключаемых модулей. Разумеется, ее можно и отключить, указав соответствующую опцию в командной строке.

Когда по каким-либо причинам нужно сохранить оригинальные имена файлов (скажем, если есть группа HTML-файлов, связанных гиперссылками, ведь при переименовании файлов гиперссылки станут недействительными) и вместе с тем требуется быстро узнать, что содержится в каждом из них, то предусмотрена возможность вместо переименования добавлять в качестве описаний извлеченные из файлов заголовки в файл descript.ion, находящийся в одном с ними каталоге (если такого файла нет, он создается). Напомню, что файл descript.ion — текстовый, содержащий однострочные описания файлов, находящихся в одном с ним каталоге. В таких файловых менеджерах, как Far, Dos Navigator, можно включить такой режим отображения, при котором рядом с именами файлов показываются их описания, взятые из этого файла. (На рисунке показано, как это выглядит в Dos Navigator при максимизированной текущей панели.)


Также был разработан подключаемый модуль mmodules.plg, возвращающий название композиции, извлеченное из музыкального файла в формате IT, XM, S3M, MOD. Да, уже существует специальная программа для переименования музыкальных файлов в соответствии с названиями композиций (MODNamer by Mauro ?DjM? Molinari), поддерживающая к тому же больше типов файлов. Но работа описываемой программы с вышеупомянутым подключаемым модулем имеет свои преимущества: передача аргументов через командную строку, отмена переименования, добавление названия композиций в качестве описаний файлов в файл descript.ion. (Текст описываемой программы см. на «Мир ПК-диске».)

Руководство пользователя

Формат команды запуска программы (в квадратных скобках — обязательные элементы, в фигурных скобках — необязательные):
[имя файла с интерпретатором Perl] [имя файла с программой]
[имя файла с подключаемым модулем] [имя обрабатываемого файла]

При этом программа будет обрабатывать один указанный файл. А чтобы обработать все файлы с заданными расширениями, находящиеся в некотором каталоге, в командной строке вместо последнего элемента (имени обрабатываемого файла) надо указать два других элемента:
[имя каталога с обрабатываемыми файлами] [список расширений обрабатываемых файлов]

Список расширений состоит из элементов, разделенных запятой (без пробела), например ?htm,html,shtml?. В нем может быть указано и пустое расширение: так, ?,htm? определяет пустое расширение и ?htm?. В списке может быть, конечно, и только одна звездочка («*»), соответствующая любой комбинации символов. Нельзя задать расширение, содержащее запятую (но, будем надеяться, вы с такой ситуацией не столкнетесь). кавычки вокруг списка обязательны, если он состоит лишь из пустого расширения (т.е. ??) или какое-то из расширений содержит пробел.

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

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

Опция «/r» — рекурсивный обход подкаталогов. Позволяет обработать сразу целую ветвь их дерева.

Опция «/n» — не создавать файл «_back_rename.pl» (это perl-скрипт для отмены переименования). По умолчанию программа создает его в каждом каталоге, где она переименовала хотя бы один файл. Причем если в каталоге уже есть такой сценарий, программа не перезапишет его, а создаст с именем «_back_rename (1).pl» и т.д. Таким образом, возможна многошаговая отмена.

Кстати, почему для обратного переименования создается именно perl-скрипт, а не просто bat-файл? Все дело в проблемах с кодировками. Внутри программы (и внутри создаваемого сценария на Perl) имена файлов хранятся в кодировке Windows-1251, а в bat-файлах используется кодировка CP866. Точное перекодирование же из Windows-1251 в CP866 невозможно из-за того, что наборы символов в этих кодировках не совпадают.

Опция «/d» — вместо переименования файлов программа будет добавлять их заголовки в качестве описаний в файл descript.ion, находящийся в каталоге с обрабатываемыми файлами (когда такового файла нет, то он создается). Конечно, если программа обрабатывает не один каталог (т.е. была указана опция «/r»), то в каждом каталоге будет свой файл descript.ion.

С командой запуска, кажется, разобрались. Теперь подробнее рассмотрим работу программы. Когда она выводит на экран имена или описания файлов, то вместо некоторых символов можно увидеть заштрихованные прямоугольники: значит, программа не смогла перекодировать данный символ из Windows-1251 (в этой кодировке хранятся имена и описания файлов) в CP866 (в ней происходит вывод на экран).

При переименовании файлов программа при необходимости укорачивает извлеченный из файла заголовок так, чтобы его длина не превышала максимальную (по умолчанию — 128 символов, это значение хранится в переменной $max_n_len). Кроме того, если в заголовке содержатся символы, недопустимые в имени файла ( / : * ? « |, а также символы с кодами 0-31), они будут заменены на «_».

Когда новое имя файла (т.е. имя и расширение) оказывается таким же, как у другого файла (или каталога), находящегося в этом же каталоге, то, чтобы избежать конфликта, к новому имени (перед расширением) добавляется подстрока « (1)». Если же и такое имя уже используется, то вместо « (1)» добавляется « (2)» и т.д. Расширение переименованного файла остается тем же, каким оно было у исходного.

В случае добавления описания в файл descript.ion программа при необходимости укорачивает описание, чтобы его длина не превышала максимальную (по умолчанию — 256 символов, это значение хранится в переменной $max_d_len). Если в заголовке содержатся символы, недопустимые в описании (таковыми считаются символы с кодами 0,10,13), они будут заменены на «_».

Так как кодировка файла описаний — CP866, а в программе описания хранятся в Windows-1251, то при записи они перекодируются. Причем вместо символов, которые программа не смогла перекодировать, записываются псевдографические символы в виде заштрихованных прямоугольников.

Когда же в файле описаний уже есть информация об обрабатываемом файле, то новые данные все равно будут добавлены, а старое не удалится (я не стал усложнять программу).

Ниже приведен пример сеанса работы с программой, когда выполняется переименование файлов.

И еще пример для случая, при котором программа не переименовывает файлы, а записывает их описания в файл descript.ion.

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

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

Подключаемые модули

Все используемые в подключаемом модуле переменные должны быть в нем объявлены, чтобы предотвратить возможный конфликт с одноименными переменными основной программы (да и директива «use strict» требует, чтобы все используемые переменные были объявлены).

Ниже приведены исходные тексты двух подключаемых модулей, о которых я уже рассказывал выше: html.plg (для обработки HTML-файлов) и mmodules.plg (для обработки музыкальных модулей). Они также могут пригодиться в качестве примеров при создании собственных подключаемых обработчиков текстовых или двоичных данных. (Их текст см. на «Мир ПК-диске».)

Как еще можно использовать программу

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

Рассмотрим пример. Пусть исходные имена файлов выглядят так:
11062005.txt, 15062005.TXT и т.п., т.е. имя обозначает некую дату (первые две цифры — число, вторые две — месяц, оставшиеся четыре — год), а расширение — txt без учета регистра букв.

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

Подключаемый модуль (файл date.plg) будет таким:

Еще один пример. Допустим, в некоем каталоге находятся файлы, имена которых различаются только числом в конце — порядковым номером файла (в частности, имя может состоять лишь из порядкового номера), например, name1, name2, . name38. Если при просмотре каталога файлы упорядочиваются по именам, то их порядок будет таким: name1, name10, name11. А хотелось бы видеть «естественный» порядок, при котором файлы располагаются по возрастанию своих номеров. Дабы добиться желаемого, следует изменить порядковые номера в именах файлов так, чтобы во всех этих номерах было одинаковое число цифр. Тогда упорядочение файлов по именам даст нужный результат: name01, name02, . name38.

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

Именно такое изменение имени файла и производит приведенный ниже подключаемый модуль (файл ext_num. plg). Перед его использованием следует установить в тексте желаемое значение n в качестве начального значения переменной $n.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* Если только вы не привелигированный пользователь и не любите забавляться с командой 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 и Ьатеу доступными в режимах чтения и записи для всех пользователей, нужно вьшолнить такую операцию:

Режим 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 и его группе:

В главе 16 вы узнаете, как преобразовать fred в 1234 и stoners в 35.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

`rename 's/\.htm/\.html/' *.htm`

Однако я хочу узнать об этом больше, особенно о s в Это. Говорят, что это выражение Perl. Я не изучил язык программирования Perl, поэтому я понятия не имею, почему существует такой аргумент 's/\.htm/\.html/'. Не могли бы вы сказать мне, что это такое? Спасибо за чтение.

3 ответа

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

Первый аргумент в команде примера (rename 's/\.htm/\.html/' *.htm) содержит части меха, где в этом случае / является разделителем этих частей :

Команда s означает замену: s/<old>/<new>/. Сопоставьте регулярное выражение с содержимым пространства шаблонов. Если найдено, замените строку с заменой на замену. Регулярное выражение, которое соответствует строкам, которые необходимо заменить. В текущем случае это только строка .htm. В большинстве регулярных выражений точка . соответствует каждому символу. В этом случае мы хотим использовать точку буквально, поэтому нам нужно избегать ее специального значения, используя обратную косую черту \ (со ссылкой на один символ). Замена sting / regexp соответствует 2. Флаг, который не представлен в текущем примере. Это может быть, например, флаг g - применить замену ко всем совпадениям к регулярному выражению, а не только к первому. Предположим, у нас есть файл с именем my.htm-file.htm: оригинальная команда rename 's/\.htm/\.html/' *.htm переименует файл таким образом: my.html-file.htm. Посмотрите на нижнюю часть ответа, как избежать этой проблемы. Добавив флаг g - rename 's/\.htm/\.html/g' *.htm - новое имя файла будет: my.html-file.html.

В соответствии с именами файлов: * может представлять любое количество символов (включая ноль, другими словами, ноль или более символов). Оболочка будет rename и передает соответствующие имена файлов в виде отдельных аргументов в rename. Таким образом, *.htm - будет соответствовать всем именам файлов в текущем пути, которые заканчиваются строкой .htm. Например, если у вас есть 1.htm, 2.htm, 3.htm, 4.htm и 5.htm, то rename 's/\.htm/\.html/' *.htm передает точно те же аргументы, что и для переименования:

Вся команда (rename 's/\.htm/\.html/' *.htm) может быть прочитана следующим образом:

Вернемся к примеру, когда у нас есть файл с именем [F31]. Возможно, мы хотим изменить последнюю часть имени файла так называемого расширения после последней точки. Для этого мы должны изменить команду rename следующим образом:

Где знак $ соответствует концу строки и буквально означает «читать назад».

^ - Соответствует нулевой строке в начале пространства шаблонов, то есть то, что появляется после того, как circumflex должен появиться в начале пространства шаблонов. (подробнее) ^ действует как специальный символ только в начале регулярного выражения или подвыражения (то есть после \( или \|). Переносимые скрипты должны избегать ^ в начале подвыражения, хотя, поскольку POSIX разрешает реализации, которые рассматривают ^ как обычный символ в этом контексте. $ - То же, что и ^, но относится к концу пространства шаблонов. $ также действует как специальный символ только в конце регулярного выражения или подвыражения (то есть до \) или \|), а его использование в конце подвыражения не переносимо.

Руководство Bash для начинающих: расширение оболочки GNU / Linux. Инструменты командной строки. Сводка: Wildcards perlre - регулярные выражения Perl. GNU: sed, a редактор потока Онлайн-тестеры регулярных выражений

s для замены. Хорошо. Спасибо за Ваш ответ. Многие люди извлекут выгоду из вашего ответа. Хорошего дня:) – Smile 1 December 2017 в 15:16 @EliahKagan: Спасибо за эту заметку! Я сделал редактирование. – pa4080 1 December 2017 в 17:27

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

Первый аргумент в команде примера (rename 's/\.htm/\.html/' *.htm) содержит части меха, где в этом случае / является разделителем этих частей :

s/regexp/replacement/[flags] Команда s означает замену: s/<old>/<new>/. Сопоставьте регулярное выражение с содержимым пространства шаблонов. Если найдено, замените строку с заменой на замену. Регулярное выражение, которое соответствует строкам, которые необходимо заменить. В текущем случае это только строка .htm. В большинстве регулярных выражений точка . соответствует каждому символу. В этом случае мы хотим использовать точку буквально, поэтому нам нужно избегать ее специального значения, используя обратную косую черту \ (со ссылкой на один символ). Замена sting / regexp соответствует 2. Флаг, который не представлен в текущем примере. Это может быть, например, флаг g - применить замену ко всем совпадениям к регулярному выражению, а не только к первому. Предположим, у нас есть файл с именем my.htm-file.htm: оригинальная команда rename 's/\.htm/\.html/' *.htm переименует файл таким образом: my.html-file.htm. Посмотрите на нижнюю часть ответа, как избежать этой проблемы. Добавив флаг g - rename 's/\.htm/\.html/g' *.htm - новое имя файла будет: my.html-file.html.

В соответствии с именами файлов: * может представлять любое количество символов (включая ноль, другими словами, ноль или более символов). Оболочка будет rename и передает соответствующие имена файлов в виде отдельных аргументов в rename. Таким образом, *.htm - будет соответствовать всем именам файлов в текущем пути, которые заканчиваются строкой .htm. Например, если у вас есть 1.htm, 2.htm, 3.htm, 4.htm и 5.htm, то rename 's/\.htm/\.html/' *.htm передает точно те же аргументы, что и для переименования:

rename 's/\.htm/\.html/' 1.htm 2.htm 3.htm 4.htm 5.htm [ ! d23]

Вся команда (rename 's/\.htm/\.html/' *.htm) может быть прочитана следующим образом:

rename `<substitute>/<the string ".htm">/<with the string ".html">/` <do it for all files in the current path that end with ".htm">

Вернемся к примеру, когда у нас есть файл с именем [F31]. Возможно, мы хотим изменить последнюю часть имени файла так называемого расширения после последней точки. Для этого мы должны изменить команду rename следующим образом:

rename 's/\.htm$/\.html/' *.htm

Где знак $ соответствует концу строки и буквально означает «читать назад».

^ - Соответствует нулевой строке в начале пространства шаблонов, то есть то, что появляется после того, как circumflex должен появиться в начале пространства шаблонов. (подробнее) ^ действует как специальный символ только в начале регулярного выражения или подвыражения (то есть после \( или \|). Переносимые скрипты должны избегать ^ в начале подвыражения, хотя, поскольку POSIX разрешает реализации, которые рассматривают ^ как обычный символ в этом контексте. $ - То же, что и ^, но относится к концу пространства шаблонов. $ также действует как специальный символ только в конце регулярного выражения или подвыражения (то есть до \) или \|), а его использование в конце подвыражения не переносимо.

Руководство Bash для начинающих: расширение оболочки GNU / Linux. Средства командной строки. Сводка: Wildcards perlre - регулярные выражения Perl. GNU: sed, a редактор потока Онлайн-тестеры регулярных выражений

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

Первый аргумент в команде примера (rename 's/\.htm/\.html/' *.htm) содержит части меха, где в этом случае / является разделителем этих частей :

s/regexp/replacement/[flags] Команда s означает замену: s/<old>/<new>/. Сопоставьте регулярное выражение с содержимым пространства шаблонов. Если найдено, замените строку с заменой на замену. Регулярное выражение, которое соответствует строкам, которые необходимо заменить. В текущем случае это только строка .htm. В большинстве регулярных выражений точка . соответствует каждому символу. В этом случае мы хотим использовать точку буквально, поэтому нам нужно избегать ее специального значения, используя обратную косую черту \ (со ссылкой на один символ). Замена sting / regexp соответствует 2. Флаг, который не представлен в текущем примере. Это может быть, например, флаг g - применить замену ко всем совпадениям к регулярному выражению, а не только к первому. Предположим, у нас есть файл с именем my.htm-file.htm: оригинальная команда rename 's/\.htm/\.html/' *.htm переименует файл таким образом: my.html-file.htm. Посмотрите на нижнюю часть ответа, как избежать этой проблемы. Добавив флаг g - rename 's/\.htm/\.html/g' *.htm - новое имя файла будет: my.html-file.html.

В соответствии с именами файлов: * может представлять любое количество символов (включая ноль, другими словами, ноль или более символов). Оболочка будет rename и передает соответствующие имена файлов в виде отдельных аргументов в rename. Таким образом, *.htm - будет соответствовать всем именам файлов в текущем пути, которые заканчиваются строкой .htm. Например, если у вас есть 1.htm, 2.htm, 3.htm, 4.htm и 5.htm, то rename 's/\.htm/\.html/' *.htm передает точно те же аргументы, что и для переименования:

rename 's/\.htm/\.html/' 1.htm 2.htm 3.htm 4.htm 5.htm [ ! d23]

Вся команда (rename 's/\.htm/\.html/' *.htm) может быть прочитана следующим образом:

rename `<substitute>/<the string ".htm">/<with the string ".html">/` <do it for all files in the current path that end with ".htm">

Вернемся к примеру, когда у нас есть файл с именем [F31]. Возможно, мы хотим изменить последнюю часть имени файла так называемого расширения после последней точки. Для этого мы должны изменить команду rename следующим образом:

rename 's/\.htm$/\.html/' *.htm

Где знак $ соответствует концу строки и буквально означает «читать назад».

^ - Соответствует нулевой строке в начале пространства шаблонов, то есть то, что появляется после того, как circumflex должен появиться в начале пространства шаблонов. (подробнее) ^ действует как специальный символ только в начале регулярного выражения или подвыражения (то есть после \( или \|). Переносимые скрипты должны избегать ^ в начале подвыражения, хотя, поскольку POSIX разрешает реализации, которые рассматривают ^ как обычный символ в этом контексте. $ - То же, что и ^, но относится к концу пространства шаблонов. $ также действует как специальный символ только в конце регулярного выражения или подвыражения (то есть до \) или \|), а его использование в конце подвыражения не переносимо.

Руководство Bash для начинающих: расширение оболочки GNU / Linux. Средства командной строки. Сводка: Wildcards perlre - регулярные выражения Perl. GNU: sed, a редактор потока Онлайн-тестеры регулярных выражений

Я собираюсь рассказать об однострочных программах на Perl. Если вы овладете однострочным Perl`ом, то можете сэкономить кучу времени (я экономлю).

Цель поста — показать как Perl можно использовать заместо find, grep, awk, sed. В конце поста будет написано зачем это надо.

Ну обо всём по порядку.

Флаги

Флаг -e
Флаг позволяет запускать перловый код прямо в консоли, эту возможность я использую для проверки какого-нибудь тестового кода.
Допустим, я хочу узнать десятичное значение шестнадцатеричного числа 0xFA23B:

Примечание. Когда однострочный перловый код запускаем из под Винды, то код нужно заключать в двойные кавычки:
, в случае Линукса/Юникса код может быть как в двойных кавычках, так и в одинарных, но в Юниксе/Линуксе случае двойных кавычек приходится экранировать знаки "$":
Примечание. После флага -e должен следовать сразу код.

Флаг -l
Это флаг делает инициализирует переменные $/ и $\ значением "\n";
Переменная $/ задаёт разделитель входных полей.
Переменная $\ задаёт, что будет выводиться после команды print.

Таким образом в конце не приходится писать print "\n";

Флаг -n
Отсюда начинается самое интересное.

Где же это можно использовать?
А вот, например, нам надо добавить к именам файлов, имена которых начинаются с цифр, расширение «bak»:

А для Windows? Пожалуйста:

Посмотрим на получившуюся программу:

chomp $_; взялся от флага -l: он совместно с -n даёт добавляет ещё и chomp $_;, а не только BEGIN < $/ = "\n"; $\ = "\n"; >

Флаг -a
Флаг -a позволяет использовать Perl как awk.

То есть каждая строка расщепляется split`ом по пробелам, и получившиеся поля кладутся в массив @F.
Разделитель полей можно поменять с помощью флага -F.

Допустим, надо вывести из файла /etc/passwd имена пользователей с их домашними директориями:

А Винде я, например, хочу узнать имена файлов в папке, которые последний раз изменял в сентябре 2009:

Флаг -p
Этот флаг делает тоже, что -n только добавляет ещё блок continue c «print $_».

Допустим, мы выводим файл /etc/passwd, попутно заменяя 3 на 6.

Вместо такого кода:

Мы можем написать:

Флаг -i
Флаг i позволяет изменять файлы.

которая в свою очередь эквивалентна этой:

В кратце поясню, что происходит, когда вызываются строки perl -i.bak -pe «код» <имя файла>. Например, мы вызываем:

Файл test.txt переименовывается в файл test.txt.bak, и создаётся новый файл test.txt. Потом в каждой строке исходного файла заменяется foo на bar, которые записываются в новый фалй test.txt (по-видимому, хоть файл и переименовали, мы всё равно имеем доступ к его строкам?)

Допустим, нужно в файле заменить \r\n на \n:

В результате этого кода получатся два файла: один — test.txt.bak, который является копией исходного, другой — test.txt, где \r\n заменено на \n.

Примечание. Если вы посмотрите внимательно на программу выше ( $extension = '.bak'; . ), то увидите, что если вызывать вот так: perl -ibak_*…, то бэкапный файл будет называться «bak_test.txt», то есть если есть звёздочка в значении параметра i, то это это значение расматривается не как расширение, а как шаблон, где звёздочка обозначает имя файла.

Флаг -M позволяет подключать модули

Например, я хочу узнать где лежит модуль CGI:

Недавно мне понадобилось сделать chmod a+x всем файлам с расширением ".cgi",
но на сервере флаг -R для chmod почему-то не работал, так вот что я сделал, что-то подобное:

Этим кодом «perl -MFile::Find -e 'finddepth(sub , ».">)'" Я вызвал функцию finddepth модуля File::Find, которая рекурсивно обошла текущую директорию и вывела полные пути файлов.

Потом грепом я взял только те файлы, которые оканчиваются на '.cgi' (-P означает, что используются перловые регулярные выражения), а следующей программой «perl -nle '`chmod a+x $_`'» я сделал права на выполнение найденым файлам.

Хотя этот код я мог бы записать так:

Заметьте, что надо использовать флаг -l, чтобы в $_, попало имя файла без "\n"

А что если надо подключить некоторые переменные или подпрограммы из подключаемого пакета в пакет main?
Тогда надо писать:

BEGIN и END

Можно использовать BEGIN и END, для действий, которые должны происходить в начале и в конце, аналогично как у awk.

Например выведем линии, состоящие из 40 знаков " perl">dir /b | perl -pe "sub line ;BEGIN;END"

Дебаг

Чтобы дебажить однострочные программы надо подключать модуль B::Deparse,

Если вы запустите:

То получите вывод:

модуль B::Deparse нужно подключать так: "-MO=Deparse", а не так: "-MB::Deparse". Видимо это сделано для того, чтобы чётко определить, что мы хотим использовать этот модуль для вывода исходного кода программы, а не просто для использования каких его либо методов в программе.

Вот так модуль B::Deparse будет использоваться как обычный модуль, вывода кода не будет:

В примерах выше я использовал MO=Deparse для вывода кода программ.

Примеры однострочных программ

Вывод количества строк в файле (аналог Юниксовского wc -l)

Здесь использован хитрый приём ">

Вывод двоичного числа

Замена \r\n на \n в файле

Примечание. Почему-то подобный код не работает в Винде: она упорно добавляет \r\n, я делал binmode ARGV,
binmode $ARGV, binmode *ARG, но ничего не помогало, буду биться дальше. Буду благодарен вам, если напишите как заменить \r\n на \n в Винде.

Преобразование IP адреса из формы «цифры-точки» в число:

Удаление папок .svn в текущей папке и её подпапках (рекурсивно)

тоже самое для Windows:

Вывод IP-aдреса в шестнадцатеричной форме

Зачем это надо?

Как и обещал напишу, зачем это всё надо. Вы можете сказать, что есть find, awk, grep, sed, зачем однострочный Perl?

Ну во-первых, в Винде, по-умолчанию, грепа и awk нету. Да конечно быстрее пользоваться grep для отбора строк, но что если надо сделать чуть по-больше, например переименовать файл? Вы скажете есть ведь find, да есть. Так что же я скажу в защиту однострочного перла?

Во-первых, если программируешь на Perl, то Perl помнишь очень хорошо и можешь сразу начать писать однострочную программу, не заглядывая в man. (Поначалу, правда, может быть немного не привычно, но когда втянешься, будет легко)

Во-вторых, бывает удобно использовать именно Perl. Например, когда я хочу иметь аналог awk (смотри флаг -a) с мощью Perl (например хочу использовать в однострочной программе функции pack, unpack или регулярные выражение Perl)

В-третьих,
Perl — мощный язык. Однострочная программа на perl — это обычная программа на Perl, только в командной строке. А значит однострочные программы на перл можно использовать для самых разных задач! (Но думаю, длинные однострочные программы, наверно, лучше не писать, лучше сделать обычный перловый скрипт).

Заключение

Не подумайте только, что я призываю отказаться от grep, find, sed или awk. Не призываю! Я сам продолжаю использовать grep, find. Просто я хотел рассказать о ещё одном полезном инструменте как «однострочный перл», который удобен для тех кто программирует на Perl, ибо: 1) не надо читать man (ты и так всё помнишь), 2) используется мощь Perl

Спасибо за внимание. Тех кто заинтересовался отсылаю сюда:

Советую гуглить перловые однострочные программы с словом: one-liners

обн:
Чтобы заменить \r\n на \n в Винде, надо просто написать:

обн:
Добавил в код от AntonShcherbinin, "s/\r|\n//g;", а то в Линуксе просто binmode не прокатывает, теперь этот код универсален: работает и в Винде, и в Линуксе.

обн (26.08.2012):
Переписал примеры кода под теги <source lang=«Perl»></source>

Читайте также: