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

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

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

1 ответ

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

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

      #!/bin/bash

#USAGE: MAIN.SH FILE FOLDER FOLDER1 FOLDER2 FOLDER3...
#WILL MOVE FILE TO FOLDER AND CHECK FOLDER1, 2 AND 3 FOR SYMLINKS

OLD_PWD=$PWD #NOT TO BE MISTAKEN WITH GOOD OL' OLDPWD
cd $2
FINAL_PWD=$PWD
cd $OLD_PWD

symlink_checker () { #checks recursively if symbolic links are inside folder. Stores links in arrays.
    local -n ARRAY=$1
    while IFS= read -r -d $'\0';
    do
        ARRAY+=("$REPLY");
    done < <(find ~+ -type l -print0)
}
###CHECKING IF FILE OR FOLDER OR NONE
if [ -d $1 ]
    then TYPE="FOLDER";
elif [ -f $1 ]
    then TYPE="FILE";
else TYPE="UNKNOWN";
fi
###GETTING SEPARATED FILE NAME AND ABSOLUTE PATH
if [ $TYPE = "FOLDER" ]
    then cd $1;
        FINAL_PATH=$PWD;
        cd -
elif [ $TYPE = "FILE" ]
    then
    JUST_FILENAME="$(basename -- $1)"
    path=`readlink -f "$1"`
    JUST_DIRNAME="$(dirname "$path")"
else
    exit 0
fi
###Using symlink checker to find symlinks
if [ -z $3 ] #if no $3, just do it in current folder
    then
        cd ./
        links=()
        symlink_checker links
else         #else, go trough every listed folder in args
    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
###Itrating over link to get separated linkname, filepath and link actual path
printf "All links detected: \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 is link to $LINK_POINTER_PATH/$LINK_POINTER_NAME"
    fi
done
printf "\nDo you wish to: redo all links (y or enter), do nothing (n) or\nprint all links in file and exit (type filename and enter)?\n\r"

read user_answer
if [[ $user_answer = "y" || $user_answer = "" || $user_answer = "Y" ]]; then
for link in ${links[@]}   #the magic happens here. Redirecting links to new filepath.
    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)"
            ##SO NOW WE HAVE ALL WE NEED TO KNOW ABOUT THE LINKS. LETS USE IT.##
            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 "File already exists. Exiting."
    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 --is link to--> $LINK_POINTER_PATH/$LINK_POINTER_NAME" >> $user_answer
        fi
    done
echo "done. No links fixed, just log written."
fi
printf "Finally move file? If there is any symlink in previous folder\n it will be overriden by file. That shouldnt be a problem,\nshould it?\ny tmo move file, n for not moving file."
read user_input

if [ $user_input = "y" || $user_input = "Y" ]; then
    echo "moving file..."
    mv $1 $2 #actually moving file
    echo "done."
    exit 0
elif [ $user_input = "n" || $user_input = "N" ]; then
    echo "Ok but all symlinks have already been moved. They are probably broken now."
    exit 0
else
    echo "Unknown answer. Exiting without moving files. Leaving symlinks broken. You can fix that by doing 'mv $1 $2'"
Другие вопросы по тегам