类似于Dropbox的bash脚本
我正在尝试编写一个bash脚本,该脚本同步两个文件夹,类似于Dropbox服务所做的工作,即删除最新文件夹中不存在的文件/文件夹,并复制最新的内容。 但是,我不确定如何处理路径/文件名。因此,我向您征求一些建议,也可能是关于改进脚本的建议。代码如下:类似于Dropbox的bash脚本,bash,synchronization,Bash,Synchronization,我正在尝试编写一个bash脚本,该脚本同步两个文件夹,类似于Dropbox服务所做的工作,即删除最新文件夹中不存在的文件/文件夹,并复制最新的内容。 但是,我不确定如何处理路径/文件名。因此,我向您征求一些建议,也可能是关于改进脚本的建议。代码如下: #!/bin/bash out=/dev/stdout #outerr=/dev/stderr outerr="test.txt" if [ $# -lt 2 ] then echo "" >> $out echo
#!/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
提前谢谢大家,
阿尔多我将脚本的内容缩减为一个循环。我想我在做你想做的事。我改变了所有:
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 argument so $subdirs is blank.
#This means that "$source/$subdirs"* = "$source/"* for the original 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会阻止我们读取通过的第一个目录。编辑后的代码将实际用于子目录和完整路径目录
您还会注意到,我在
cp
、rm
和mkdir
调用的末尾添加了2>>$outerr
。这将捕获到stderr的任何输出,并将其写入$outerr
。请记住,所有这些命令都可能失败,因此至少捕获错误是很重要的。在测试模式下,您正在打印将要用于$outerr
的所有实际命令。当不处于测试模式时,您根本没有使用它,因此我用处理过程中可能出现的任何错误填充它。最后,我们检查-s$outerr
,查看是否报告了任何错误,并发布一点警告。如果您想安全起见,可以将2>$outerr
更改为2>$outerr | | exit 2
,以强制退出任何错误。如果您开发此脚本是为了作业或只是为了学习如何编写bash脚本,那么Jason已经做出了非常有用的回答。另外,请忽略我文章的其余部分,因为它没有回答你的问题
如果您只需要一个用于同步两个文件夹的工具,则rsync
适合您。
例如,我使用以下脚本将音乐同步到外部驱动器
#!/bin/sh
rsync -aE --delete /home/user/Music/ /media/user/data/my.lib/music
--delete
用于删除源文件夹树中不存在的目标文件和文件夹。还有一个选项用于在同步完成后删除这些文件(delete after
)。您也可以使用--dry run
选项,以便不执行任何实际更改。
-aE
用于递归到文件夹中,保留文件权限、修改时间、可执行性、链接、所有者和组
为什么不构建自己的脚本?原因有三。首先,没有理由重新发明轮子。其次,像
rsync
这样的工具经过了良好的测试和调试。第三,我们正在讨论您的文件…;-) 您是否知道rsync
?据我所知,它不会删除源文件中不存在的目标文件夹中的文件。--从dest dirs中删除无关文件。
。因此,最好不要删除路径中的反斜杠?我发现这样更容易。代码中存在一些逻辑错误,结果变成cp/
,显然会失败。这是由于源列表通过管道传输到sed,而不是目标列表。因此,这些列表是不同的,但它们的逻辑是相同的。我选择这种方法是因为它消除了嵌套for循环。越简单越好,尤其是我重新分配了IFS。对不起。错误在这里cp$source/$filesrc$dest/$filedest
。其中,这将扩展到正确的源文件,但目标文件将扩展到/
。我想练习并拥有某种交互式文件到文件的操作选择。出于实际目的,坚持rsync会更健壮(更安全!)