Dropbox-подобный bash-скрипт - PullRequest
0 голосов
/ 23 мая 2018

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

#!/bin/bash

out=/dev/stdout
#outerr=/dev/stderr
outerr="test.txt"

if [  $# -lt 2 ]
then
    echo "" >> $out
    echo "   Not enough arguments passed to the script, try again next time." >> $out
    echo "" >> $out
    echo "   Usage: " >> $out
    echo "   bash files_sync.sh [--t] SOURCE_NEW DESTINATION_OLD " >> $out
    echo "" >> $out
    echo "   Options: --t  just prints the modifications, without taking any action" >> $out
    echo "" >> $out
    exit 1
fi

test=0

if [ $# -eq 2 ]
then 
    source=$(printf '%s\n' "$1" | sed  's/\///')
    dest=$(printf '%s\n' "$2")
    #dest=$(printf '%s\n' "$2" | sed  's/\///')
fi

if [ $# -eq 3 ]
then 
    if [ $1 == "--t" ]
    then
        echo "" >> $out
        echo "   Printing the suggested actions..." >> $out
        test=1 
        source=$(printf '%s\n' "$2" | sed  's/\///')
        dest=$(printf '%s\n' "$3")
    else
        echo "   What's your game, dude? Exiting. " >> $out
        exit 20
    fi
    #source=$(printf '%s\n' "$2" | sed  's/\///')
    #dest=$(printf '%s\n' "$3" | sed  's/\///')
fi

if [ ! -d $source ]; then 
    echo "" >> $out
    echo "   Path $source does not exists or not a folder, exiting. " >>         $out
    echo "" >> $out
    exit 2
else
    echo "" >> $out
    echo "   (SOURCE) UP-TO-DATE FOLDER: $source" >> $out
    echo "" >> $out
fi

if [ ! -d $dest ]; then 
    echo "   Path $dest does not exists or not a folder, exiting. " >>     $out
    echo "" >> $out
    exit 3
else
    echo "   (DESTINATION) TO BE SYNCHED FOLDER: $dest" >> $out
    echo "" >> $out
fi

echo -n "   Do you REALLY want to proceed? [y/N] : " >> $out
read choice
echo "" >> $out

if [ $choice == "n" ]; then
    echo "   Brilliant, bye!" >> $out
    echo "" >> $out
    exit 4
fi

if [ $choice == "y" ]
then
    echo "   Awesome! Let's get started ...."
    #FIRST CHECK IF DEST FILES ARE IN SOURCE. IF NOT DELETING THEM

    list1=`find $source -mindepth 1 -name "*" | sed 's/'"$source"'\///'`
    list2=`find $dest -mindepth 1 -name "*"`
    #list2=`find $dest -mindepth 1 -name "*" | sed 's/'"$dest"'\///'` 

    outdest="testdest.txt"
    echo "  Writing DEST files into $outdest file" >> $out
    > $outdest
    for filedest in $list2
    do
        echo $filedest >> $outdest
    done
    outsrc="testsrc.txt"
    echo "  Writing SRC files into $outsrc file" >> $out
    > $outsrc
    for filesrc in $list1
    do
        echo $filesrc >> $outsrc
    done

    outerr="testerr.txt"
    >$outerr
    echo "  Writing operations into $outerr file" >> $out

    for filedest in $list2
    do
        #if [ ! -d $filedest ]
        #then
            check="true"
            for filesrc in $list1
            do
                #if [ ! -d $filesrc ]
                #then
                    if [ $filedest == $filesrc ]
                    then
                        check="false"
                        break
                    fi
                #fi
           done
                if [ $check == "true" ]
                then 
                if [ $test -eq 1 ]
                then        
                    echo "rm $dest/$filedest"  >> $outerr
                else
                    echo " ! "; rm $dest/$filedest
                fi
            fi
    #fi
    done
    #THEN CHECK IF FILE IN SOURCE IS IN DEST. IF NOT COPY ELSE CHECK IF IT IS NEWER
    for filesrc in $list1
    do
        check="true"
        for filedest in $list2
        do              
            if [ $filedest == $filesrc ]
            then
                check="false"
                if [ $source/$filesrc -nt $dest/$filedest ] 
                then 
                    if [ -d $source/$filesrc ]
                    then
                        echo " folder here, do nothing"  >> $outerr
                    else
                        if [ $test -eq 1 ]
                        then        
                            echo "cp $source/$filesrc $dest/$filedest"  >> $outerr
                        else
                            echo " ! "; cp $source/$filesrc $dest/$filedest
                        fi

                    fi
                fi
                #break
            fi
        done
        if [ $check == "true" ]
        then
            if [ $test -eq 1 ]
            then        
                #echo -n "   +++ $source/$filesrc not existing in DEST : "  >> $outerr
                if [ -d $source/$filesrc ]
                then
                    echo "mkdir $dest/$filesrc"  >> $outerr
                else
                    echo "mv $source/$filesrc $dest/ "  >> $outerr
                fi
            else
                if [ -d $source/$filesrc ]
                then
                    echo " ! "; mkdir $source/$filesrc
                else
                    echo " ! "; mv $source/$filesrc $dest/$filedest
                fi
            fi
        fi
    done

    echo ""  >> $out
    echo '   --------- Sync COMPLETE ! -------------' >> $out
    echo ""  >> $out
   else
        echo ""  >> $out
        echo '   Not getting the answer. Exiting'  >> $out
        echo ""  >> $out
   fi

Заранее спасибо,

Альдо

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Если вы разрабатываете этот сценарий для задания или просто для того, чтобы научиться кодировать сценарий bash, то Джейсон уже сделал очень полезный ответ.Также, пожалуйста, игнорируйте оставшуюся часть моего поста, так как он не отвечает на ваш вопрос.

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

#!/bin/sh
rsync -aE --delete /home/user/Music/  /media/user/data/my.lib/music

--delete используется для удаления целевых файлов и папок, которых нет в дереве исходных папок.Существует также возможность удаления этих файлов после завершения синхронизации (delete-after).Вы также можете использовать опцию --dry-run, чтобы не вносить никаких изменений.-aE предназначен для повторного использования в папках, сохранения прав доступа к файлам, времени модификации, исполняемости, ссылок, владельца и группы.

Почему бы не создать свой собственный скрипт?По трем причинам.Во-первых, нет причин изобретать велосипед.Во-вторых, такой инструмент, как rsync, хорошо протестирован и отлажен.В-третьих, мы говорим о ваших файлах ...; -)

0 голосов
/ 23 мая 2018

Я сократил содержание сценария до одного цикла.Я думаю, что делаю то, что вы хотите.Я изменил все:

if [ something ]
then

на if [something ]; then.Я думаю, что это сделало все отступы легче читать.Я удалил логику предварительного удаления и добавил цикл после завершения копирования.Таким образом, весь этот цикл пропал:

for filedest in $list2
    do
        #if [ ! -d $filedest ]
        #then
            check="true"
            for filesrc in $list1
            do
                #if [ ! -d $filesrc ]
                #then
                    if [ $filedest == $filesrc ]
                    then
                        check="false"
                        break
                    fi
                #fi
           done
                if [ $check == "true" ]
                then 
                if [ $test -eq 1 ]
                then        
                    echo "rm $dest/$filedest"  >> $outerr
                else
                    echo " ! "; rm $dest/$filedest #logic error here.
                fi
            fi
    #fi
    done

Еще одно важное изменение - все цитирование и изменение внутреннего разделителя полей (IFS).(Изменить: я изменил с помощью find на glob (*), поэтому нет необходимости менять IFS) Все это необходимо для того, чтобы вы могли обрабатывать имена файлов с пробелами в них.Вот что я закончил:

#!/bin/bash

out=/dev/stdout
#outerr=/dev/stderr
#outerr="test.txt"


function sync_dir 
{
    #This is our current subdirectory of $source and $dest.
    #Note: In the original call we do not pass an arguement so $subdirs is blank.
    #This means that "$source/$subdirs"* = "$source/"* for the origninal call.
    local subdirs="$1"

    #By removing these find calls, we can just use * and avoid making changes to the IFS
    #local list1=`find "$source/$subdirs" -mindepth 1 -maxdepth 1 -name "*"`
    #local list2=`find "$dest/$subdirs" -mindepth 1 -maxdepth 1 -name "*"`

    #No need for find.  Let * do all the work.
    for filesrc in "$source/$subdirs"*; do 
        echo "$filesrc" >> $outsrc
        #remove path from $filesrc
        local filewithoutdir="$(basename "$filesrc")"           
        #if "$filesrc" is a directory
        if [ -d "$filesrc" ]; then
            #check to see if the destination is a directory. Make it if not...
            if [ ! -d "$dest/$subdirs$filewithoutdir" ]; then   
                if [ $test -eq 1 ]; then            
                    echo "mkdir \"$dest/$subdirs$filewithoutdir\"" >> $outerr
                else
                    echo " ! "; mkdir "$dest/$subdirs$filewithoutdir" 2>> $outerr
                fi
            fi
            #recursive call if we are dealing with a directory.
            sync_dir "$subdirs$filewithoutdir/"
        #else if not a file OR "$filesrc" is newer than the destination, copy it.
        elif [ ! -f "$dest/$subdirs$filewithoutdir" -o "$filesrc" -nt "$dest/$subdirs$filewithoutdir" ]; then 
            if [ $test -eq 1 ]; then        
                echo "cp \"$filesrc\" \"$dest/$subdirs$filewithoutdir\""  >> $outerr
            else
                echo " ! "; cp "$filesrc" "$dest/$subdirs$filewithoutdir" 2>> $outerr
            fi
        fi
    done
    #Here we loop throught the destination directory
    for filedest in "$dest/$subdirs"*; do
        echo "$filedest" >> $outdest
        local filewithoutdir="$(basename "$filedest")"
        #If the files do not exist in the source directory, delete them.
        if [ ! -e "$source/$subdirs$filewithoutdir" ]; then
            if [ $test -eq 1 ]; then        
                echo "rm -f \"$filedest\" >/dev/null 2>&1"  >> $outerr
            else
                #We allow this to fail and silence it because it may try to delete a directory.
                echo " ! "; rm -f "$filedest" >/dev/null 2>&1
            fi

        fi
        #if you want to remove files and directories use this, but this can be dangerous.
        #if [ ! -e $source/$filewithoutdir ]; then
        #    if [ $test -eq 1 ]; then        
        #        echo "rm -rf \"$filedest\" >/dev/null 2>&1"  >> $outerr
        #    else
        #        echo " ! "; rm -rf "$filedest" 2>> $outerr
        #    fi
        #    rm -rf "$filedest" >/dev/null 2>&1
        #fi
    done  
}

if [  $# -lt 2 ]; then
    echo "" >> $out
    echo "   Not enough arguments passed to the script, try again next time." >> $out
    echo "" >> $out
    echo "   Usage: " >> $out
    echo "   bash files_sync.sh [--t] SOURCE_NEW DESTINATION_OLD " >> $out
    echo "" >> $out
    echo "   Options: --t  just prints the modifications, without taking any action" >> $out
    echo "" >> $out
    exit 1
fi

test=0

if [ $# -eq 2 ]; then
    #This will cause an issue when processing full path like /home/user/bla
    #source=$(printf '%s\n' "$1" | sed  's/\///')
    #dest=$(printf '%s\n' "$2")
    #dest=$(printf '%s\n' "$2" | sed  's/\///')
    source="$1"
    dest="$2"
fi

if [ $# -eq 3 ]; then
    if [ $1 == "--t" ]; then
        echo "" >> $out
        echo "   Printing the suggested actions..." >> $out
        test=1 
        source="$2"
        dest="$3"
    else
        echo "   What's your game, dude? Exiting. " >> $out
        exit 20
    fi
fi

if [ ! -d $source ]; then 
    echo "" >> $out
    echo "   Path $source does not exists or not a folder, exiting. " >>         $out
    echo "" >> $out
    exit 2
else
    echo "" >> $out
    echo "   (SOURCE) UP-TO-DATE FOLDER: $source" >> $out
    echo "" >> $out
fi

if [ ! -d $dest ]; then 
    echo "   Path $dest does not exists or not a folder, exiting. " >>     $out
    echo "" >> $out
    exit 3
else
    echo "   (DESTINATION) TO BE SYNCHED FOLDER: $dest" >> $out
    echo "" >> $out
fi

echo -n "   Do you REALLY want to proceed? [y/N] : " >> $out
read choice
echo "" >> $out

if [ $choice == "n" ]; then
    echo "   Brilliant, bye!" >> $out
    echo "" >> $out
    exit 4
fi

if [ $choice == "y" ]; then
    echo "   Awesome! Let's get started ...."

    outdest="testdest.txt"
    echo "  Writing DEST files into $outdest file" >> $out
    > $outdest

    outsrc="testsrc.txt"
    echo "  Writing SRC files into $outsrc file" >> $out
    > $outsrc

    outerr="testerr.txt"    
    echo "  Writing operations into $outerr file" >> $out
    >$outerr

    #function call
    sync_dir

    #If we are not in test mode and errors have occured, they will be written to $outerr.
    #This test (-s) checks to see that $outerr is not zero length (implies -e).
    if [ ! $test -eq 1 -a -s $outerr ]; then
      echo "WARNING: Errors have occured during the running of this script.  See $PWD/$outerr for details" >> $out
    fi

    echo ""  >> $out
    echo '   --------- Sync COMPLETE ! -------------' >> $out
    echo ""  >> $out
else
    echo ""  >> $out
    echo '   Not getting the answer. Exiting'  >> $out
    echo ""  >> $out
fi

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

Редактирование: теперь реализована рекурсивная версия.Моя ранняя версия на самом деле неверна.Я неправильно понял -mindepth как -maxdepth.-maxdepth не позволил бы нам прочитать переданный первый каталог.Отредактированный код будет фактически работать с подкаталогами и каталогами полного пути.

Вы также заметите, что я добавил 2>> $outerr к концу вызовов cp, rm и mkdir.Это перехватит любой вывод в stderr и запишет его в $outerr.Помните, что все эти команды могут потерпеть неудачу, поэтому важно как минимум отловить ошибки.В тестовом режиме вы печатали все фактические команды, которые будут использоваться в $outerr.Вы не использовали его вообще, когда не в тестовом режиме, поэтому я заполняю его любыми ошибками, которые могут возникнуть во время обработки.В конце мы проверяем -s $outerr, чтобы увидеть, были ли обнаружены какие-либо ошибки, и выложить небольшое предупреждениеЕсли вы хотите быть осторожнее, измените 2>> $outerr на 2>> $outerr || exit 2, чтобы принудительно завершить работу при любых ошибках.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...