Tips & tricks — Починка битых символьных ссылок (утилита для юзера — урок для начинающего программера)
У меня иногда так бывает, что после разбора завалов в домашнем каталоге и
перемещения файлов на новые более логичные для них места, требуется
восстановить поломанные ссылки на эти перемещённые файлы.
Чтобы не делать этого ручками, набросал три скрипта: один на перле и два на
шелле.
Статья написана в том числе для начинающий программистов, с подробными
объяснениями алгоритмов, принятых и применённых решений в спойлерах.
Итак, декомпозиция задачи на три:
1) Найти все битые ссылки,
2) Для заданной ссылки найти места, где может находится «потерянный» ссылкой файл,
3) Починить ссылку, переписав её местоназначение.
Программистские подробности об алгоритмах и тонкостях реализации в спойлере,
если кому интересно:
Самый сложный шаг №2. Для него был разработан такой алгоритм (и воплощён в
перле, т.к. на шелле такое сделать довольно сложно):
1) Разбить полный путь к «потерянному» (ныне несуществующему) файлу на имя
файла (basename) и каталог (путь к файлу без самого файла, dirname),
2) Разбить путь к файлу на компоненты-каталоги и найти первый каталог в этой
цепочке, который реально отсутствует, таким образом все каталоги до этого
каталога существуют в системе. Это та точка, из которой будет вестись поиск,
назовём её guessed_dir.
3) Найти все файлы с заданным именем (basename) рекурсивно в подкаталогах
guessed_dir.
4) Если файлов в шаге 3 не найдено, поднимемся на каталог выше guessed_dir
(guessed_dir = guessed_dir/..) и повторим шаг 3.
5) Останов.
В результате выполнения этого алгоритма находится файл с заданным именем, если
он был перемещён практически в любое место файловой системы, причём поиск будет
очень быстрым, т.к. в первую очередь будет щупаться самая узкая область:
предполагается, что файл был перемещён в пределах самого узкого существующего
каталога, а расширяться область поиска будет только в том случае, если файл был
перемещён куда-то выше.
На деле я использую небольшую модификацию этого алгоритма:
1) я оптимизирую поиск при поднятии на каталог выше, исключая из поиска уже
проверенные более «узкие» каталоги,
2) я не останавливаюсь на первом найденном файле, стараюсь найти как можно
больше вариантов и даю юзеру (то есть себе, любимому =) как можно больше
вариантов для выбора и корректного восстановления файлов.
Теперь к делу.
1) Найти все битые ссылки.
Скрипт fixlinks:
2) Для заданной ссылки найти места, где может находится «потерянный» ссылкой файл.
Скрипт guess, сердце всего решения:
3) Починить ссылку, переписав её местоназначение.
Скрипт fixlink, промежуточная связка между fixlinks и guess:
Резюме для начинающих программистов в спойлере.
1) Учимся декомпозировать задачу и выбирать средства реализации.
Часто оказывается, что мелкие простые подзадачи легче всего решаются на
разных языках. В этом примере части, связанные с поиском файлов и перепрошитием
ссылок, легче всего решить шелловскими утилитами вроде find и ln, поэтому они
реализованы на shell, в то время, как задача по поиску и «догадыванию» нового
имени файла достаточно сложная и её проще было решить на более сложном и полном
языке; в моём случае я выбрал perl.
2) Учимся упрощать.
Скрипту на perl передаются очищенные данные: полностью корректный
полный путь к несуществующему файлу, полученный с помощью «readlink -m».
Сделать то же самое средствами чистого перл было бы сложнее, поэтому я этой сложности
избежал, водрузив эту работу на инструменты, изначально для этого предназначенные.
This is a true unix-way!
3) Учимся расширять знания на основе уже имещихся.
Раньше я не знал, что у readlink есть ключ «-m», который преобразует
относительный путь, вычитанный в символьной ссылке, в полный корректный путь,
разворачивая «.» и «..», без наложения необходимости существования на каждый
элемент пути. Однако я знал о существовании команды readlink, и знал, что мне
нужно получить полное имя файла по ссылке на него. Дальнейшее решение было
получено чтением мануалов по readlink. Если бы этой опции не было, пошёл бы
копать в сторону sed/awk/basename/dirname, в гугл пошёл бы в конце концов.
Учитесь опираться на существующие знания и получать новые для достижения чётко
поставленной цели: чем чётче цель, тем больше шансов найти решение за краткие
сроки! А иногда пять минут чтения мануалов избавляют от получасового написания
и отладки программы на том же perl/sed/awk.
4) Учимся документировать.
После написания каждого скрипта, к ним была написана справка по пользованию для
простого юзера. Это действительно важно, и не только если вы собираетесь
распространять скрипты другим людям, но и для себя. Предтавьте себя через
неделю, месяц, год... Вы уже забыли, что и как было написано, но вот встала
такая я же или похожая задача. Всё, что вы помните, это то, что когда-то для
решения этой задачи написали какие-то скрипты. А задачу надо решить быстро, и
нет времени вспоминать, как и что вы писали год назад и где эти скрипты лежат.
Если бы справки не было, вы бы даже если и нашли эти скрипты, то потратили бы
кучу времени, вспоминая, что и как было сделано и как это пользовать. Но вы
написали справку! И теперь эти скриптики мало того, что можно в крайнем случае
склероза найти полнотекстным поиском по файлам по ключевым словам «ссылки,
починить» (да тем же грепом, в конце концов), но и воспользоваться ими сможете,
что называется, с места в карьер: прочитали краткую справку и запустили
скрипты, не вдаваясь в подробности реализации.
перемещения файлов на новые более логичные для них места, требуется
восстановить поломанные ссылки на эти перемещённые файлы.
Чтобы не делать этого ручками, набросал три скрипта: один на перле и два на
шелле.
Статья написана в том числе для начинающий программистов, с подробными
объяснениями алгоритмов, принятых и применённых решений в спойлерах.
Итак, декомпозиция задачи на три:
1) Найти все битые ссылки,
2) Для заданной ссылки найти места, где может находится «потерянный» ссылкой файл,
3) Починить ссылку, переписав её местоназначение.
Программистские подробности об алгоритмах и тонкостях реализации в спойлере,
если кому интересно:
Самый сложный шаг №2. Для него был разработан такой алгоритм (и воплощён в
перле, т.к. на шелле такое сделать довольно сложно):
1) Разбить полный путь к «потерянному» (ныне несуществующему) файлу на имя
файла (basename) и каталог (путь к файлу без самого файла, dirname),
2) Разбить путь к файлу на компоненты-каталоги и найти первый каталог в этой
цепочке, который реально отсутствует, таким образом все каталоги до этого
каталога существуют в системе. Это та точка, из которой будет вестись поиск,
назовём её guessed_dir.
3) Найти все файлы с заданным именем (basename) рекурсивно в подкаталогах
guessed_dir.
4) Если файлов в шаге 3 не найдено, поднимемся на каталог выше guessed_dir
(guessed_dir = guessed_dir/..) и повторим шаг 3.
5) Останов.
В результате выполнения этого алгоритма находится файл с заданным именем, если
он был перемещён практически в любое место файловой системы, причём поиск будет
очень быстрым, т.к. в первую очередь будет щупаться самая узкая область:
предполагается, что файл был перемещён в пределах самого узкого существующего
каталога, а расширяться область поиска будет только в том случае, если файл был
перемещён куда-то выше.
На деле я использую небольшую модификацию этого алгоритма:
1) я оптимизирую поиск при поднятии на каталог выше, исключая из поиска уже
проверенные более «узкие» каталоги,
2) я не останавливаюсь на первом найденном файле, стараюсь найти как можно
больше вариантов и даю юзеру (то есть себе, любимому =) как можно больше
вариантов для выбора и корректного восстановления файлов.
Теперь к делу.
1) Найти все битые ссылки.
Скрипт fixlinks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
2) Для заданной ссылки найти места, где может находится «потерянный» ссылкой файл.
Скрипт guess, сердце всего решения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
|
3) Починить ссылку, переписав её местоназначение.
Скрипт fixlink, промежуточная связка между fixlinks и guess:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Резюме для начинающих программистов в спойлере.
1) Учимся декомпозировать задачу и выбирать средства реализации.
Часто оказывается, что мелкие простые подзадачи легче всего решаются на
разных языках. В этом примере части, связанные с поиском файлов и перепрошитием
ссылок, легче всего решить шелловскими утилитами вроде find и ln, поэтому они
реализованы на shell, в то время, как задача по поиску и «догадыванию» нового
имени файла достаточно сложная и её проще было решить на более сложном и полном
языке; в моём случае я выбрал perl.
2) Учимся упрощать.
Скрипту на perl передаются очищенные данные: полностью корректный
полный путь к несуществующему файлу, полученный с помощью «readlink -m».
Сделать то же самое средствами чистого перл было бы сложнее, поэтому я этой сложности
избежал, водрузив эту работу на инструменты, изначально для этого предназначенные.
This is a true unix-way!
3) Учимся расширять знания на основе уже имещихся.
Раньше я не знал, что у readlink есть ключ «-m», который преобразует
относительный путь, вычитанный в символьной ссылке, в полный корректный путь,
разворачивая «.» и «..», без наложения необходимости существования на каждый
элемент пути. Однако я знал о существовании команды readlink, и знал, что мне
нужно получить полное имя файла по ссылке на него. Дальнейшее решение было
получено чтением мануалов по readlink. Если бы этой опции не было, пошёл бы
копать в сторону sed/awk/basename/dirname, в гугл пошёл бы в конце концов.
Учитесь опираться на существующие знания и получать новые для достижения чётко
поставленной цели: чем чётче цель, тем больше шансов найти решение за краткие
сроки! А иногда пять минут чтения мануалов избавляют от получасового написания
и отладки программы на том же perl/sed/awk.
4) Учимся документировать.
После написания каждого скрипта, к ним была написана справка по пользованию для
простого юзера. Это действительно важно, и не только если вы собираетесь
распространять скрипты другим людям, но и для себя. Предтавьте себя через
неделю, месяц, год... Вы уже забыли, что и как было написано, но вот встала
такая я же или похожая задача. Всё, что вы помните, это то, что когда-то для
решения этой задачи написали какие-то скрипты. А задачу надо решить быстро, и
нет времени вспоминать, как и что вы писали год назад и где эти скрипты лежат.
Если бы справки не было, вы бы даже если и нашли эти скрипты, то потратили бы
кучу времени, вспоминая, что и как было сделано и как это пользовать. Но вы
написали справку! И теперь эти скриптики мало того, что можно в крайнем случае
склероза найти полнотекстным поиском по файлам по ключевым словам «ссылки,
починить» (да тем же грепом, в конце концов), но и воспользоваться ими сможете,
что называется, с места в карьер: прочитали краткую справку и запустили
скрипты, не вдаваясь в подробности реализации.