Переместить файлы, сохраняя символические ссылки.

Вопрос или проблема

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

Есть ли способ достичь этого, кроме как воссоздавать ссылки после перемещения файлов?

Я написал скрипт, который пытается решить эту проблему.

Я не привык к программированию на bash. На самом деле, я в основном изучал bash, пока делал это, так что, возможно, это не является лучшим кодом на свете. Но он работал в моих тестах. Чтобы искать по всей системе, вам следует запускать его, указывая “/” в качестве сканируемой папки.

#!/bin/bash

#ИСПОЛЬЗОВАНИЕ: MAIN.SH ФАЙЛ ПАПКА ПАПКА1 ПАПКА2 ПАПКА3...
#БУДЕТ ПЕРЕМЕЩАТЬ ФАЙЛ В ПАПКУ И ПРОВЕРЯТЬ ПАПКИ 1, 2 И 3 НА НАЛИЧИЕ СИМВОЛИЧЕСКИХ ССЫЛОК

OLD_PWD=$PWD #НЕ ПУТАТЬ С ДОБРЫМ СТАРЫМ OLDPWD
cd $2
FINAL_PWD=$PWD
cd $OLD_PWD

symlink_checker () { #рекурсивно проверяет, находятся ли символические ссылки внутри папки. Сохраняет ссылки в массивы.
    local -n ARRAY=$1
    while IFS= read -r -d $'\0';
    do
        ARRAY+=("$REPLY");
    done < <(find ~+ -type l -print0)
}
###ПРОВЕРКА, ЯВЛЯЕТСЯ ЛИ ФАЙЛ ПАПКОЙ, ФАЙЛОМ ИЛИ НИ ТЕМ, НИ ДРУГИМ
if [ -d $1 ]
    then TYPE="ПАПКА";
elif [ -f $1 ]
    then TYPE="ФАЙЛ";
else TYPE="НЕИЗВЕСТНО";
fi
###ПОЛУЧЕНИЕ ОТДЕЛЬНОГО ИМЕНИ ФАЙЛА И АБСОЛЮТНОГО ПУТИ
if [ $TYPE = "ПАПКА" ]
    then cd $1;
        FINAL_PATH=$PWD;
        cd -
elif [ $TYPE = "ФАЙЛ" ]
    then
    JUST_FILENAME="$(basename -- $1)"
    path=`readlink -f "$1"`
    JUST_DIRNAME="$(dirname "$path")"
else
    exit 0
fi
###Использование symlink checker для поиска символических ссылок
if [ -z $3 ] #если нет $3, просто делать это в текущей папке
    then
        cd ./
        links=()
        symlink_checker links
else         #если нет, пройти через каждую указанную папку в аргументах
    iterator=2
        at_array=( $@ )
        links=()
    while [ ! -z ${at_array[$iterator]} ]
        do
            cd ${at_array[$iterator]}
            symlink_checker links
            cd $OLD_PWD
            iterator=$iterator+1
        done
fi
###Итерация по ссылкам, чтобы получить отдельное имя ссылки, путь файла и фактический путь ссылки
printf "Все обнаруженные ссылки: \n \n"
for link in ${links[@]}
do
    link_readlink=`readlink -f "$link"`
    actual_thing=`readlink -f "$OLD_PWD/$1"`
    if [ "$link_readlink" = "$actual_thing" ]; then
        LINK_NAME="$(basename -- $link)"
        LINK_PATH="$(dirname "$link")"
        LINK_ALMOST_POINTER_PATH=`readlink "$link"`
        LINK_POINTER_PATH="$(dirname "$LINK_ALMOST_POINTER_PATH")"
        LINK_POINTER_NAME="$(basename -- $LINK_ALMOST_POINTER_PATH)"
        echo "$LINK_PATH/$LINK_NAME является ссылкой на $LINK_POINTER_PATH/$LINK_POINTER_NAME"
    fi
done
printf "\nЖелаете ли вы: воссоздать все ссылки (y или enter), ничего не делать (n) или\nнапечатать все ссылки в файле и выйти (введите имя файла и нажмите enter)?\n\r"

read user_answer
if [[ $user_answer = "y" || $user_answer = "" || $user_answer = "Y" ]]; then
for link in ${links[@]}   #волшебство происходит здесь. Перенаправление ссылок на новый путь.
    do
        link_readlink=`readlink -f "$link"`
        actual_thing=`readlink -f "$OLD_PWD/$1"`
        if [ "$link_readlink" = "$actual_thing" ]; then
            LINK_NAME="$(basename -- $link)"
            LINK_PATH="$(dirname "$link")"
            LINK_ALMOST_POINTER_PATH=`readlink "$link"`
            LINK_POINTER_PATH="$(dirname "$LINK_ALMOST_POINTER_PATH")"
            LINK_POINTER_NAME="$(basename -- $LINK_ALMOST_POINTER_PATH)"
            ##ПОЭТОМУ ТЕПЕРЬ МЫ ЗНАЕМ ВСЕ, ЧТО НАМ НУЖНО ЗНАТЬ О ССЫЛКАХ. ДАВАЙТЕ ИСПОЛЬЗОВАТЬ ЭТО.##
            if [[ "$LINK_POINTER_PATH/$LINK_POINTER_NAME" = *".."* ]]; then
                cd $LINK_PATH
                current_try="$LINK_POINTER_PATH"
                while [ ! "`readlink -f $current_try/$2`" = "`readlink -f $FINAL_PWD`" ]; do
                    current_try=${current_try%..*}
                done
                unlink $LINK_NAME
                ln -s "$current_try/$2/$1" $LINK_NAME
                cd -
            else
                cd $LINK_PATH
                unlink $LINK_NAME
                ln -s "$FINAL_PWD/$LINK_POINTER_NAME" $LINK_NAME
                cd -
            fi
        fi
    done
exit 0
fi
if [[ $user_answer = "n" || $user_answer = "N" ]]; then
    exit 0
fi
if [[ ! $user_answer = "" || $user_answer = "*" ]]; then
if test -f "$user_answer"; then
    echo "Файл уже существует. Выход."
    exit 0
fi
touch $user_answer
for link in ${links[@]}
    do
        link_readlink=`readlink -f "$link"`
        actual_thing=`readlink -f "$OLD_PWD/$1"`
        if [ "$link_readlink" = "$actual_thing" ]; then
            LINK_NAME="$(basename -- $link)"
            LINK_PATH="$(dirname "$link")"
            LINK_ALMOST_POINTER_PATH=`readlink "$link"`
            LINK_POINTER_PATH="$(dirname "$LINK_ALMOST_POINTER_PATH")"
            LINK_POINTER_NAME="$(basename -- $LINK_ALMOST_POINTER_PATH)"
            echo "$LINK_PATH/$LINK_NAME -- является ссылкой на --> $LINK_POINTER_PATH/$LINK_POINTER_NAME" >> $user_answer
        fi
    done
echo "выполнено. Ссылки не исправлены, просто запись в журнале."
fi
printf "Наконец, переместить файл? Если в предыдущей папке есть какие-либо символические ссылки,\n они будут переписаны файлом. Это не должно быть проблемой,\nне так ли?\ny для перемещения файла, n для отказа от перемещения файла."
read user_input

if [ $user_input = "y" || $user_input = "Y" ]; then
    echo "перемещение файла..."
    mv $1 $2 #фактическое перемещение файла
    echo "выполнено."
    exit 0
elif [ $user_input = "n" || $user_input = "N" ]; then
    echo "Ок, но все символические ссылки уже перемещены. Вероятно, они теперь сломаны."
    exit 0
else
    echo "Неизвестный ответ. Выход без перемещения файлов. Оставление символических ссылок сломанными. Вы можете исправить это, выполнив 'mv $1 $2'"

Ответ или решение

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

Теория

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

Пример

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

Применение

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

Сценарий Bash

Сценарий, представленный в вопросе, принимает следующие параметры:

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

Основная функция сценария:

  • Определяет, является ли первый аргумент файлом или директорией.
  • Использует команду find для поиска всех символических ссылок.
  • Переписывает символические ссылки, чтобы они указывали на новый путь перемещаемого файла.

Вот некоторые важные аспекты сценария:

  1. Определение типа файла: Использование команды, чтобы понять, является ли аргумент папкой или файлом, гарантирует корректное обращение к объекту.
  2. Поиск символических ссылок: Команда find в bash используется для рекурсивного поиска всех символических ссылок в указанных территориях.
  3. Перенастройка символических ссылок: Сценарий обеспечивает переадресацию символической ссылки на новое место нахождения файла, тем самым избегая её слом.

Объяснение шагов

Сценарий организован следующим образом:

  • Он устанавливает переменные пути для текущей директории и директории назначения.
  • Использует symlink_checker функцию для сбора всех символических ссылок в предоставленных папках.
  • Путем циклического перебора каждой символической ссылки, сценарий сравнивает пути, и если они совпадают с перемещаемым файлом, перенаправляет символическую ссылку на новое местоположение.
  • Сценарий предоставляет выбор пользователю, желает ли он перезаписать ссылки или просто выдать их в файл и ничего не изменять.
  • Последним шагом идет физическое перемещение файла в новое местоположение, при необходимости.

Примеры использования

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

Чтобы сценарий работал более понятно и эффективно, рекомендуется:

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

Такой скрипт может значительно облегчить управление файлами и ресурсами благодаря грамотному подходу к переадресации символических ссылок. Также этот процесс подчеркивает важность управления конфигурацией в системах и необходимость автоматизации повседневных задач в IT.

Оцените материал
Добавить комментарий

Капча загружается...