![]() |
Модераторы: korob2001, ginnie |
![]() ![]() ![]() |
|
korob2001 |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2871 Регистрация: 29.12.2002 Репутация: 31 Всего: 61 |
Очень часто люди задают один и тот же вопрос: Как удалить не пустой каталог?
Ответ прост: нужно удалить сначала его содержимое, ну, а затем, удалить и интересующий нас каталог. Если вам не достаточно этого ответа, читайте дальше. Как мы знаем, в Perl, как, впрочем, и в других ( многих ) языках программирования невозможно удалить каталог одной командой, если в нём находятся файлы или другие вложенные каталоги. Как Вы, наверное, знаете, для удаления каталогов в Perl используется функция rmdir('имя каталога'). Для начала, давайте напишем программу, которая будет удалять список каталогов, переданных ей в качестве параметра через командную строку. Итак, что должна делать наша программа? 1. Проверить, был ли передан ей хотя бы один параметр. a) Если хотя бы один параметр есть, то переходим к пункту 2. б) Если нет, значит выводим юзеру сообщение о том, как правильно запускать нашу программу и завершаем работу. 2. Затем циклически пройти по всем параметрам и удалять необходимый каталог, не забывая перед этим проверить каталог на существование и, что он действительно является каталогом, а не файлом. По идее, нам нужно было бы сделать проверку на то, что каталог пуст. Так как при попытке удалить не пустой каталог мы получим сообщение об ошибке и аварийное завершение программы. Давайте всё-таки сделаем эту проверку, вот для чего: дело в том, что наша программа рассчитана как на один параметр, так и на список параметров, в качестве которых выступают имена удаляемых каталогов. Другими словами, если наша программа получает ,скажем, имена пяти каталогов которые необходимо удалить и, скажем, второй каталог оказался не пустым, и наша программа не делает данную проверку, то в результате мы удалим первый каталог, затем программа перейдёт ко второй итерации цикла и, здесь она завершает свою работу, так как каталог оказался не пустым и не было данной проверки. Соответственно, остальные 3 каталога не будут удалены, потому как наша прога аварийно завершила свою работу на второй итерации цикла. Вот начальный код нашей программы:
Теперь давайте сохраним всю эту бодягу в файл с расширением .pl. Желательно создать для этой программы отдельный каталог, так как Perl не перемещает, при удалении, файлы и папки в корзину, после удаления, восстановить их будет практически невозможно. Теперь создайте несколько каталогов с произвольными именами. Например: C:\test1 D:\test2 Допустим, наша программа называется remcat.pl. Теперь открываем командную строку и вводим следующую команду: C:\>perl remcat.pl C:/test1 D:/test2 После чего переданные каталоги будут удалены. С чем Вас и поздравляю! Теперь на диске C:\ создайте каталог с именем test и в него положите файл с именем test.txt ( можно пустой ). Теперь попробуйте удалить этот каталог с помощью нашей программы. C:\>perl remcat.pl C:/test После чего вы получите сообщение об ошибке. А теперь давайте, наконец-то, перейдём к сути. Как же все-таки удалить не пустой каталог? Способов, как всегда, несколько 1. Можно воспользоваться системными возможностями через system() или обратные кавычки, если это позволяет ваша система. Но в таком случае, мы оказываемся привязанными к определённой платформе. 2. Можно заюзать какой-нибудь модуль. Например: File::Path или File::Find. Решение довольно не плохое. 3. Можно воспользоваться своими ручками и накатать небольшую програмку, которая будет работать с нормальной скоростью, и без участия сторонних модулей. Решения: Пункт 1 даже описывать не буду. Потому как, команда, передаваемая оболочке, зависит от конкретной OS. Пункт 2 очень даже не плох. Итак, попробуем модифицировать нашу программу с помощью модуля File::Path. Если вы не любите ковыряться в коде, а любите результаты, то это для вас. Исходный код:
Всё, что мы сделали, так это только в начале программы подключили модуль File::Path и заменили стандартную функцию rmdir() на функцию модуля File::Path, rmtree(). Прошу обратить внимание на то, что в качестве параметра функции передаётся анонимный массив. Это позволяет существенно оптимизировать код, но с другой стороны, мы лишимся информации о каждом действии программы. Если передаётся один параметр, то его можно передать при помощи простого скаляра. В нашем случае можно опустить квадратные скобки. Пункт 3 Ладно, всё это хорошо, но давайте, наконец, перейдём к пункту 3. Не знаю как вам, но меня не очень впечатляет такое программирование как в пункте 2. Хотя многие меня не поймут, но я всё же предпочитаю иногда поработать руками, а то, если всегда пользоваться готовыми "велосипедами", перестаёшь чувствовать себя настоящим "велосипедистом". :hehe Итак, а вот и ручная работа. Постараюсь данный код максимально прокомментировать ниже:
Как можно заметить, у нас всего лишь добавилась функция RemoveTree(). Теперь давайте я постараюсь пояснить каждую строку нашей программы. Для облегчения я пронумеровал строки, естественно вам этого делать не нужно. 1. Как и в любой Perl программе нужно указать путь к интерпритатору Perl. 2. Подключаем прагму use strict. Она не обязательна, но желательна, дело в том, что её рекомендуется использовать в каждой программе. Она как бы ужесточает Perl, заставляя нас заранее объявлять все переменные, запрещает использование в программе символических ссылок, а так же перед использованием подпрограмм, декларировать их. Можно эти запреты выставлять по отдельности. 3. Начинается условный оператор if, с помощью которого мы проверяем наличие переданных параметров в нашу программу. 4. Если параметры переданы, то запускаем подпрограмму RemoveDir() и в качестве параметра передаём ей массив @ARGV ( в нём содержатся параметры из переданные через командную строку ). 5. Вступает в роль вторая часть условного оператора if .... else. 6. Если параметры не переданы, то выводим сообщение юзеру о том, как правильно нужно запускать программу. 7. После того, как выдано сообщение, делаем выход из программы. 8. Заканчивается условный оператор if ... else. 9. Начинается подпрограмма RemoveDir(). 10. Начинается цикл, который продолжает свои итерации до тех пор, пока не закончатся параметры, переданные подпрограмме. Как вам, наверное, известно, они передаются в специальный массив @_. 11 - 17. Начинается условный оператор if ... else, который проверяет, существует ли данный каталог и действительно ли он является каталогом. Если условие истинно, то пытаемся удалить его, если удаление каталога прошло успешно, то выводим сообщение о том, что данный каталог успешно удалён, в противном случае в действие вступает ещё один условный оператор unless с постусловием. То есть, в строке 14 происходит сразу 3 действия. Разделим их на 3 пункта a, b и c. a. ) Вызывается подпрограмма RemoveTree(). Ей в качестве параметра передаётся имя каталога который не смогла удалить подпрограмма RemoveDir(), то есть та, в которой мы сейчас находимся. После этого весь процесс передаётся подпрограмме RemoveTree(), а текущая подпрограмма ожидает завершения работы вызванной подпрограммы, для дальнейшего продолжения своей работы. b. ) Затем, после окончания работы, подпрограмма RemoveTree() возвращает ложное значение. Это можно увидеть в строке 37. Это означает, что подпрограмма успешно завершила свою работу. c. ) Если мы получили 0 в качестве возвращаемого значения, то, это означает, что каталог в данный момент пуст и готов к удалению. После того, как подпрограмма RemoveDir() получила значение 0, с помощью unshift мы заносим имя текущего каталога, который только что был очищен, в начало массива @_. Теперь цикл переходит на следующую итерацию и первым оказывается тот каталог, который мы на предыдущей итерации очистили и подготовили для удаления. 18. В данной строке просто заканчивается цикл while. 19. Конец подпрограммы RemoveDir(). 20. Просто комментарий. 21. Начинается подпрограмма RemoveTree(). 22. Начинается цикл аналогичный циклу в строке 10. 23. Создаём локальную переменную типа typeglob, она нам будет необходима для работы с каталогами, в качестве дескриптора каталога. 24. Подпрограмма пытается открыть очередной каталог из массива @_. Если происходит какая-либо ошибка, то юзер получает соответствующее сообщение, и программа завершает свою работу. 25. Начинается второй, вложенный, цикл. Здесь переменной $file поочерёдно присваиваются имена всех файлов и каталогов открытого нами каталога в строке 24. Как только прочитан весь каталог, внутренний цикл завершает свою работу. И управление передаётся внешнему циклу. 26. Здесь всё просто, переходим к следующей итерации цикла, если полученное имя файла состоит из одной или двух точек. Такие файлы, точнее каталоги являются: одна точка - текущий каталог, две точки - родительский каталог, который находится выше в иерархии данной ветви каталогов. Проверка осуществляется с помощью регулярных выражений. Что такое регулярные выражения, объяснять не буду, так как это довольно сложная и объёмная тема, и она не относится к рассматриваемому вопросу. 27 - 33. Вот здесь следует быть особо внимательным. Начинается условный оператор if ... else. Здесь мы проверяем, является ли полученный файл каталогом. Если да, то здесь начинается ещё одна условная конструкция, которая подобна конструкции if ... else. Но имеет более компактный вид: УСЛОВИЕ ? Выражение1 : Выражение 2; . Эту конструкцию Perl унаследовал от языка C, как, впрочем, и многое другое. Теперь я кратко поясню, что есть что. В данной конструкции УСЛОВИЕ - это условное выражение, в нашем случае на этом месте стоит rmdir("имя_удаляемого_каталога"). У вас может возникнуть вполне резонный вопрос: А где же условие? Условие-это и есть rmdir(), который пытается удалить каталог и, если ему это удалось, то он возвращает истинное значение, в противном случае он возвратит ложь. То есть, если каталог успешно удалён, то выполнится Выражение1, в противном случае Выражение2. Ну ладно, я думаю, все всё поняли, если нет, то никогда не поздно задать вопрос по данной теме. Ещё хочу добавить, что я записал эту конструкцию в трёх строках, хотя можно было её записать и на одной. Это я сделал для наглядности, и для более простого пояснения. Итак, вернёмся к нашей программе. Выше описанная конструкция пытается удалить каталог, имя которого находится в переменной $file. Если каталог успешно удалён, то он был пуст, и цикл переходит на следующую итерацию, для получения нового имени из открытого каталога. Если же каталог не был удалён, значит программа считает, что он тоже не пустой и добавляет его в конец массива @_, для последующего открытия и обработки, и цикл переходит к следующей итерации. Но это все происходит при том условии, что полученное имя файла является каталогом. Смотрите строку 27. Это к тому, что, это может быть и файл, в данном случае программа пытается удалить полученный файл, строка 32. Если файл не удалось удалить, то программа "умирает с музыкой". Этот термин придумал не я. Это означает - программа завершает свою работу с выводом поясняющего сообщения, почему так, а не иначе. Почему здесь, да и в некоторых других местах, программу необходимо завершить? Потому что, если нам не удалось удалить файл, который может быть занят другим процессом, то нам и не удастся удалить и каталог, в котором он находится. Отсюда вывод, незачем продолжать выполнение программы, заранее зная, что она не будет завершена успешно. Гораздо разумнее оповестить юзера о том, что произошло, и завершить выполнение программы. 34. Заканчивается вложенный цикл. 35. Программа пытается закрыть открытый ранее каталог или "умирает с музыкой". 36. Завершается внешний цикл. 37. Подпрограмма RemoveTree() возвращает значение 0, то есть ложь. 38. Завершается подпрограмма RemoveTree(). Ну, вот вроде и всё. Теперь сохраните данный код в файл, я назвал его remcat.pl, вы вправе назвать её так, как вам больше нравится, и попробуйте запустить её, передав ей в качестве параметров пути к каталогам, которые необходимо удалить. Желательно перед этим сделать резервные копии удаляемых каталогов, так как я уже говорил ранее, что после удаления файла или каталога Perl программой, восстановить его будет практически невозможно. Все примеры, как и сама статья, написаны мной, и вы можете с ними делать всё что пожелаете. Код, конечно, можно было оптимизировать ( сократить до одной подпрограммы, так как вы, наверное, уже заметили, что в двух наших подпрограммах есть много похожих действий и не составит особого труда сделать одну подпрограмму, которая по объёму будет гораздо меньше двух рассмотренных ), но у меня была цель сделать данные примеры максимально понятными, а не короткими или быстрыми. Можно было бы воспользоваться и другими возможностями Perl, для достижения аналогичного результата, например: можно добиться того же эффекта с помощью замыканий. Изначально, я хотел изложить только суть того, как удалять каталоги с содержимым, но по-моему у меня не получилось краткое пояснение. Но, я думаю, вам это пригодится, я имею в виду то, что не относится к данной теме. Например, описание конструкции УСЛОВИЕ ? Выражение1 : Выражение 2, прагмы use strict и т.п. Пожелания и угрозы, а так же вопросы, пишите на E-mail: [email protected] или в этот форум. С наилучшими пожеланиями, korob2001 -------------------- "Время проходит", - привыкли говорить вы по неверному пониманию. "Время стоит - проходите вы". |
||||||
|
|||||||
![]() ![]() ![]() |
Правила форума "Perl" | |
|
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Perl: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |