Shell脚本实现文件系统同步器

文件同步软件是一种自动使两个文件树同步的软件。市场上有功能非常完善的文件同步器。为了简化该项目的实施,将仅开发同步器以同步同一台计算机上存在的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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值