文件同步软件是一种自动使两个文件树同步的软件。市场上有功能非常完善的文件同步器。为了简化该项目的实施,将仅开发同步器以同步同一台计算机上存在的2个文件系统。
1.操作
同步器的目的使树A和树B具有相同的状态,即如果树A的文件p(记为p / A)存在,则树B的文件p(记为p / B)的也存在并且状态应该相同(数据和元数据),反之亦然。 元数据表示p文件的类型,权限,p的大小以及对p的最后修改日期。
通常,如果在文件的两边都进行了编辑,则不可能实现完全同步; 也就是说文件的两个版本之间存在冲突。
文件同步器处理两个文件树,分别称为A和B,以及一个包含上次成功同步日志的文件。 例如,存储在$ HOME / .synchro中的日志文件将包含路径A和B; 另外,对于每个没有冲突而同步的文件p / A = p / B,日志文件应包含路径p,文件p的类型和权限,文件p的大小以及文件p的最后修改日期。
2.简单的同步器
同步器平行穿过两个树A和B。 对于任何p文件,它将执行以下操作:
-如果p / A是目录,而p / B是普通文件,反之亦然,则存在冲突。
-否则,如果p / A和p / B都是目录,则它递归地下降。
-否则,如果p / A和p / B是两个具有相同模式,大小和最后修改日期的普通文件,则同步成功,并且没有任何关系。
-否则,如果p / A符合日志文件,而p / B不符合日志文件,则后者已更改; 然后必须将内容,模式和最后修改日期从p / B复制到p / A。
-相反,如果p / B符合日志文件,而p / A不符合日志文件,则必须从p / A到p / B进行复制。
-最后,如果p / A和p / B都是普通文件,并且它们都不符合日志文件(要么是因为没有p的条目,要么是因为 这与两个文件的元数据不对应),存在冲突。
然后,同步器将使用已成功同步的所有普通文件的数据重写日志文件。
请注意,这两个树在文件系统中不一定以相同的顺序出现:A可以包含文件p和q,但顺序不一样,而B包含q和p。
您还可以自行决定如何处理冲突。 同步完成后,我们可以简单地显示冲突列表。 我们可以要求用户做出选择。 如果文件是文本文件,我们可以显示两个文件之间的差异(通过执行diff命令)。
3.带有内容比较的同步器
如果在两边都对文件进行了相同的编辑,则上述同步器将指示虚假冲突。 如果两个普通文件之间存在冲突,则内容比较同步器将比较这两个文件的内容; 如果相同
-如果两个文件的元数据也相同,则无事可做,则同步成功;
-如果两个文件之一的元数据与日志中存储的相同,则另一个文件的元数据已更改; 您只需要更改第一个的元数据,同步就可以成功;
-如果元数据不同,则两个文件有冲突,但仅在元数据上存在冲突; 让用户知道可能很有用。
当然,如果在上述情况之一下同步成功,则必须将结果存储在日志文件中。
下面为脚本代码以及注释:
#!/bin/bash -i
#路径结尾处“/”不可省略
Path_A=~/Projet/Atree/ #文件系统A路径
Path_B=~/Projet/Btree/ #文件系统B路径
Path_L=~/HOME/.synchro/Journal.log #日志文件路径
#=================================================================================================
#同步脚本目录下文件(同步时临时文件)
Log_A=~/Projet/Sychroniseur/Log_A #记录路径A文件信息
Log_B=~/Projet/Sychroniseur/Log_B #记录路径B文件信息
Temp_log=~/Projet/Sychroniseur/Temp_log
#==================================================================================================
#递归读取所给目录下所有文件名
read_dir(){
for i in `find $1 -print`
do
if [ ${i/$1/} ]
then
printf "${i/$1/}\n"
fi
done
}
#清空文件内容
reset(){
>$1
}
#初始化Journal文件,若该文件不存在,则创建一个
initialize_log(){
if [ -e $Path_L ]
then
echo "Start Sychroniseur....... "
else
touch $Path_L
reset $Path_L
fi
if [ -e $Log_A ]
then
echo ""
else
touch $Log_A
reset $Log_A
fi
if [ -e $Log_B ]
then
echo ""
else
touch $Log_B
reset $Log_B
fi
}
#获取文件详细信息(包括最后修改时间,文件属性,文件大小,文件路径等)
get_information(){
#get the timestamp
stat -t $1$3 | awk '{ printf "%d\t", $13}'
if [ -d $1$3 ]
then
ls -ld $1$3 | cut -c1 | awk '{ printf "%s\t", $1}'
ls -ld $1$3 | cut -c2-10 | awk '{ printf "%s\t", $1}'
ls -ld $1$3 | awk '{ printf "%s\t%s-%s-%s\t" , $5, $6, $7, $8 }'
else
ls -l $1$3 | cut -c1 | awk '{ printf "%s\t", $1}'
ls -l $1$3 | cut -c2-10 | awk '{ printf "%s\t", $1}'
ls -l $1$3 | awk '{ printf "%s\t%s-%s-%s\t" ,$5, $6, $7, $8 }'
fi
printf "$1$3\t\t"
printf "$2$3\n"
}
#每个文件的同步操作完成后,更新日志文件
update_log(){
grep -v "$1$line" $Path_L > $Temp_log
cat $Temp_log>$Path_L
for i in `find $1$3`
do
echo $i
name=${i/$1/}
get_information $1 $2 $name>>$Path_L
done
echo "update $1$3 in Journal!"
}
#从Journal日志中根据路径关键字来获得日志中有关文件的信息
get_log(){
echo $(grep $1$2 $Path_L | awk '{print $1,$2,$3,$4}')
}
#获取文件修改时间
get_mtime(){
echo $(stat -c %Y $1)
}
#获取文件元数据
get_metadata(){
echo $(get_information $1 $2 $3 | awk '{print $1,$2,$3,$4}')
}
#获取文件权限信息
get_file_authority(){
echo $(get_information $1 $2 $3 | awk '{print $2, $3}')
}
#获取Journal日志文件中记录的文件权限信息
get_log_authority(){
echo `grep $1$2 $Path_L | awk '{print $2, $3}'`
}
#处理冲突,主要用于当两个文件同时被更改导致无法根据题设中判定需求完成同步,需要人为选择保存其中一个文件
handle_conflict(){
echo "Input: -1- to delete $1$3, save $2$3. "
echo " -2- to save $1$3, delete $2$3. "
echo " -3- to delete both files. "
read -n1 -p "enter number > " input </dev/tty
case $input in
1)
rm -rf $1$3
handle_metadata $2 $1 $3
echo "Delete $1$3, save $2$3 !!!";;
2)
rm -rf $2$3
handle_metadata $1 $2 $3
echo "Delete $2$3, save $1$3 !!!";;
3)
rm -rf $1$3
rm -rf $2$3
handle_metadata $1 $2 $3
echo "Delete both files !!!";;
*)
echo "mistype!!!! Please input again."
handle_conflict $1 $2 $3
esac
}
#处理冲突使用
handle_metadata(){
cp -rpf $1$3 `dirname $2$3`
echo "copy $1$3 to $2$3 !!"
update_log $1 $2 $3
}
#该部分为该文件同步器主要操作函数
#使用时输入3个参数: Path_1 Path_2 Log_X //Path_1为此次比较目录,Path_2为此次被比较目录,Log_X为记录比较目录信息的日志文件
compare_file(){
while read line
do
echo ""
echo "=================================================="
echo ""
echo $line
if [ -e $2$line ] #判断被比较目录中该文件是否存在
then
#被比较目录中同名文件存在
echo "$2$line exist!!"
if [[ -f $1$line && -f $2$line ]] #两文件是否均为普通文件
then
echo "$1$line and $2$line are ordinary files "
#比较两文件与日志文件中的详细信息(包括最后修改时间,文件属性,文件大小,文件路径等)
if [[ `get_metadata $1 $2 $line` = `get_log $1 $line` && `get_metadata $2 $1 $line` = `get_log $2 $line` ]]
then
#两文件与日志中的各项信息均相同,无需同步
echo "file $line in A and B are not changed!!!"
elif [[ `get_metadata $1 $2 $line` = `get_log $1 $line` || `get_metadata $2 $1 $line` = `get_log $2 $line` ]]
then
#两文件中只有一个与日志文件中的各项信息相同,则另一个文件已被修改过,将被修改的文件进行同步
#分别比较两文件的最后修改时间以及文件权限来完成同步
echo "compare `get_metadata $1 $2 $line` and `get_log $1 $line`"
echo "compare `get_metadata $2 $1 $line` and `get_log $2 $line`"
if [ `get_mtime $1$line` -ne `get_mtime $2$line` ]
then
echo "mtime od $1$line and $2$line are not same!"
if [ `get_mtime $1$line` -gt `get_mtime $2$line` ]
then
echo "mtime of $1$line greater than $2$line!! "
handle_metadata $1 $2 $line
else
echo "mtime of $2$line greater than $1$line!! "
handle_metadata $2 $1 $line
fi
else
echo "mtime of $1$line and $2$line are same but metadata are not same!"
if [[ `get_file_authority $1 $2 $line` = `get_log_authority $1 $line` && `get_file_authority $2 $1 $line` != `get_log_authority $2 $line` ]]
then
echo "The authority of $2$line has been changed!!"
handle_metadata $2 $1 $line
elif [[ `get_file_authority $1 $2 $line` != `get_log_authority $1 $line` && `get_file_authority $2 $1 $line` = `get_log_authority $2 $line` ]]
then
echo "The authority of $1$line has been changed!!"
handle_metadata $1 $2 $line
else
echo "Other metadata has been changed!!"
fi
fi
else
#此时两文件的详细信息均与日志中记载的不同,故说明两文件均被修改,故产生冲突
#首先比较两文件内容以及元数据,若相同,则无需进行冲突处理,默认保存拥有最新的最后修改时间的文件
#若两文件的内容不同或者权限不同则要进行冲突处理
echo "compare `get_metadata $1 $2 $line` and `get_log $1 $line`"
echo "compare `get_metadata $2 $1 $line` and `get_log $2 $line`"
echo "Erreur: $1$line and $2$line have confilt."
if [[ -r $1$line && -r $2$line ]] #文件是否可读
then
if [ "`diff $1$line $2$line`" = "" ]
then
echo "File $1$line and $2$line have same content!!"
if [[ "`get_file_authority $1 $2 $line`" = "`get_log_authority $1 $line`" && "`get_file_authority $2 $1 $line`" = "`get_log_authority $2 $line`" ]]
then
echo "File $1$line and $2$line have same metadata!!"
if [ `get_mtime $1$line` -ge `get_mtime $2$line` ]
then
echo "Metadata of $1$line greater than $2$line"
handle_metadata $1 $2 $line
else
echo "Metadata of $2$line greater than $1$line"
handle_metadata $2 $1 $line
fi
elif [[ "`get_file_authority $1 $2 $line`" = "`get_log_authority $1 $line`" && "`get_file_authority $2 $1 $line`" != "`get_log_authority $2 $line`" ]]
then
echo "File $1$line and Journal have same metadata but $2$line not!!"
handle_metadata $2 $1 $line
elif [[ "`get_file_authority $1 $2 $line`" != "`get_log_authority $1 $line`" && "`get_file_authority $2 $1 $line`" = "`get_log_authority $2 $line`" ]]
then
echo "File $2$line and Journal have same metadata but $1$line not!!"
handle_metadata $1 $2 $line
else
echo "The authority of $line are not same!!"
echo "The authority of $1$line is `get_file_authority $1 $2 $line`"
echo "The authority of $2$line is `get_file_authority $2 $1 $line`"
handle_conflict $1 $2 $line
fi
else
echo "File $1$line and $2$line have not same content!!"
diff -c $1$line $2$line
handle_conflict $1 $2 $line
fi
else
echo "File $1$line and $2$line can not be read!!"
handle_metadata $1 $2 $line
fi
fi
else
#比较两文件类型,处理一个文件为目录文件,而另一个文件为普通文件的这种情况
if [[ -d $1$line && -f $2$line ]]
then
echo "Erreur: $1$line is directory file but $2$line is ordinary file!!"
handle_conflict $1 $2 $line
elif [[ -f $1$line && -d $2$line ]]
then
echo "Erreur: $2$line is directory file but $1$line is ordinary file!!"
handle_conflict $2 $1 $line
elif [[ -d $1$line && -d $2$line ]]
then
echo "Files $1$line and $2$line are directory files!!"
else
echo "Files $1$line and $2$line are other types!!!!"
fi
fi
else
#被比较文件不存在,通过查询日志文件来获知该文件为新建文件还是待删除文件
echo "$2$line not found!"
if [ -e $1$line ]
then
if [ "`grep $1$line $Path_L`" != "" ]
then
rm -rf $1$line
echo "Don\'t find $1$line in $2 but in Journal so delete $1$line"
update_log $1 $2 $line
else
echo "Do not find $1$line in $2 and in Journal, so it is a new file."
handle_metadata $1 $2 $line
fi
else
echo "$1$line is not exist!! "
fi
fi
done<$3
printf "\n\n"
}
#==================================================================================================
#该比较器需要进行两次比较,分别将路径A作为比较路径,路径B作为被比较路径以及路径A作为比较路径,路径B作为被比较路径
printf "===========First compare===========\n\n"
initialize_log
cat $Path_L
read_dir $Path_A > Log_A
compare_file $Path_A $Path_B $Log_A
printf "===========Second compare===========\n\n"
cat $Path_L
read_dir $Path_B > Log_B
compare_file $Path_B $Path_A $Log_B
#===================================================================================================
echo "=====================open Journal======================"
cat $Path_L
#更新两个临时日志文件
read_dir $Path_A > Log_A
read_dir $Path_B > Log_B