利用Vim进行文件夹对比的三种方式
前言
最近经常使用vim, 心血来潮想研究了一下如何用Vim进行代码merge. 在Windows下有Beyond Compare和WinMerge等软件,可以比较两个目录结构及文件内容的异同,并以图形界面的形式呈现给用户。Vim有的vimdiff可以进行文件内容的对比和merge操作,但是很遗憾的是只针对单个文件比较,文件夹的比较就无能为力了。
在网上这方面的介绍不多,只搜到https://blog.****.net/littlewhite1989/article/details/45312081。该文章介绍了一种linux脚本结合Vim进行文件对比的方法,但是原脚本有些地方没有考虑周全,所以对其进行了一些优化。比如文件名或者文件夹名里面有空格等特殊符的情况有异常,只在文件夹B里面的文件或者目录漏掉了。
在区分两个文件是否相同时,原文是用md5sum命令实现,在子文件较多时处理时间比较长。这部分可以用linux自带的diff命令进行区分,据此进行了脚本优化。
基于md5sum的文件夹对比
基本思想参考https://blog.****.net/littlewhite1989/article/details/45312081,以下脚本是对原方法的优化。
#!/bin/bash
if [ $# -ne 2 ];then
echo "Usage:$0 dir1 dir2"
exit 1
fi
if [ ! -d "$1" -o ! -d "$2" ];then
echo "$1 or $2 is not derectory!"
exit 1
fi
## 注意,Mac的readlink程序和GNU readlink功能不同,Mac需要下载greadlink
arg1=`readlink -f "$1"`
arg2=`readlink -f "$2"`
tmp_dir=$PREFIX/tmp/tmp.$$
rm -rf $tmp_dir
mkdir -p $tmp_dir || exit 0
#echo $tmp_dir
trap "rm -rf $tmp_dir; exit 0" SIGINT SIGTERM
## 注意,Mac和Linux的MD5程序不同,请根据需求使用,这里是Mac版的用法
function get_file_md5
{
if [ $# -ne 1 ];then
echo "get_file_md5 arg num error!"
return 1
fi
local file=$1
md5sum "$file" | awk -F" " '{print $1}'
}
function myexit
{
rm -rf $tmp_dir
exit 0
}
function show_diff
{
if [ $# -ne 1 ];then
return 1
fi
local diff_file=$1
echo "diff file:"
printf " %-55s %-52s\n" "$arg1" "$arg2"
if [ -f $tmp_dir/A_only_file ];then
awk '{printf(" [%2d] %-50s\n", NR, $0)}' $tmp_dir/A_only_file
python -c 'print("-"*100)'
fi
if [ -f $tmp_dir/B_only_file ];then
awk '{for(i=0;i<60;i++)printf(" "); printf(" [%2d] %-50s\n",NR, $0)}' $tmp_dir/B_only_file
python -c 'print("-"*100)'
fi
if [ -f $diff_file ];then
awk '{printf(" [%2d] %-50s %-50s\n", NR, $0, $0)}' $diff_file
echo "(s):show diff files (a):open all diff files (q):exit"
echo
fi
}
function check_value
{
local diff_file=$1
local value=$2
tmp_file=$tmp_dir/tmp_file
>$tmp_file
for numbers in `echo "$value" | tr ',' ' '`
do
nf=`echo "$numbers" | awk -F"-" '{print NF}'`
if [ $nf -ne 1 -a $nf -ne 2 ];then
return 1
fi
begin=`echo "$numbers" | awk -F"-" '{print $1}'`
end=`echo "$numbers" | awk -F"-" '{print $2}'`
if [ -z "$end" ];then
sed -n $begin'p' $diff_file >> $tmp_file
else
if [ "$end" -lt $begin ];then
return 1
fi
sed -n $begin','$end'p' $diff_file >> $tmp_file
fi
if [ $? -ne 0 ];then
return 1
fi
done
awk -v dir1="$arg1" -v dir2="$arg2" '{
if (NR==1)
{
printf("edit %s/%s\nvertical diffsplit %s/%s\n", dir1, $0, dir2, $0)
}
else
{
printf("tabnew %s/%s\nvertical diffsplit %s/%s\n", dir1, $0, dir2, $0)
}
}' $tmp_file
}
#############################################################
# 获取diff info
#############################################################
for file1 in `find "$arg1" | sed 's% %#%g'`
do
file=`echo $file1 | sed 's%#% %g'`
file_relative_name=${file#$arg1/}
file "$file" | grep -Eqv "directory"
if [ $? -ne 0 ];then
continue
fi
if [ -f "$arg2/$file_relative_name" ];then
file "$arg2/$file_relative_name" | grep -Eqv "directory"
if [ $? -ne 0 ];then
continue
fi
md5_1=`get_file_md5 "$file"`
md5_2=`get_file_md5 "$arg2/$file_relative_name"`
if [[ "$md5_1" = "$md5_2" ]];then
continue
fi
## file not same
echo "$file_relative_name" >> $tmp_dir/diff_file
else
echo "$file_relative_name" >> $tmp_dir/A_only_file
fi
done
for file1 in `find "$arg2" | sed 's% %#%g'`
do
file=`echo $file1 | sed 's%#% %g'`
file_relative_name=${file#$arg2/}
file "$file" | grep -Eqv "directory"
if [ $? -ne 0 ];then
continue
fi
if [ ! -f "$arg1/$file_relative_name" ];then
echo "$file_relative_name" >> $tmp_dir/B_only_file
fi
done
#############################################################
# 根据输入标签打开用vim打开文件比较diff
#############################################################
if [[ ! -f $tmp_dir/diff_file && ! -f $tmp_dir/A_only_file && ! -f $tmp_dir/B_only_file ]];then
echo folders are the same!
myexit
fi
show_diff $tmp_dir/diff_file
while true
do
if [ -f $tmp_dir/diff_file ];then
echo -n "Please choose file number list (like this:1,3-4,5):"
else
echo "No diff files,enter 'q' to exit!"
echo -n ":"
fi
read value
if [[ "$value" = "s" ]] || [[ "$value" = "S" ]];then
show_diff $tmp_dir/diff_file
continue
elif [[ "$value" = "q" ]] || [[ "$value" = "Q" ]];then
myexit
elif [[ "$value" = "a" ]] || [ "$value" = "A" ];then
value="1-$"
fi
vim_script=`check_value $tmp_dir/diff_file "$value" 2>/dev/null`
if [ $? -ne 0 ];then
echo "invalid parameter[$value]!"
else
vim -c "$vim_script"
fi
done
基于diff的文件夹对比
#!/bin/bash
if [ $# -ne 2 ];then
echo "Usage:$0 dir1 dir2"
exit 1
fi
if [ ! -d "$1" -o ! -d "$2" ];then
echo "$1 or $2 is not derectory!"
exit 1
fi
## 注意,Mac的readlink程序和GNU readlink功能不同,Mac需要下载greadlink
arg1=`readlink -f "$1"`
arg2=`readlink -f "$2"`
tmp_dir=$PREFIX/tmp/tmp.$$
rm -rf $tmp_dir
mkdir -p $tmp_dir || exit 0
#echo $tmp_dir
trap "rm -rf $tmp_dir; exit 0" SIGINT SIGTERM
## 注意,Mac和Linux的MD5程序不同,请根据需求使用,这里是Mac版的用法
function get_file_md5
{
if [ $# -ne 1 ];then
echo "get_file_md5 arg num error!"
return 1
fi
local file=$1
md5sum $file | awk -F" " '{print $1}'
}
function myexit
{
rm -rf $tmp_dir
exit 0
}
function show_diff
{
if [ $# -ne 1 ];then
return 1
fi
local diff_file=$1
echo "diff file:"
printf " %-55s %-52s\n" "$arg1" "$arg2"
if [ -f $tmp_dir/A_only_file ];then
awk '{printf(" [%2d] %-50s\n", NR, $0)}' $tmp_dir/A_only_file
python -c 'print("-"*100)'
fi
if [ -f $tmp_dir/B_only_file ];then
awk '{for(i=0;i<60;i++)printf(" "); printf(" [%2d] %-50s\n",NR, $0)}' $tmp_dir/B_only_file
python -c 'print("-"*100)'
fi
if [ -f $diff_file ];then
awk '{printf(" [%2d] %-50s %-50s\n", NR, $0, $0)}' $diff_file
echo "(s):show diff files (a):open all diff files (q):exit"
echo
fi
}
function check_value
{
local diff_file=$1
local value=$2
tmp_file=$tmp_dir/tmp_file
>$tmp_file
for numbers in `echo "$value" | tr ',' ' '`
do
nf=`echo "$numbers" | awk -F"-" '{print NF}'`
if [ $nf -ne 1 -a $nf -ne 2 ];then
return 1
fi
begin=`echo "$numbers" | awk -F"-" '{print $1}'`
end=`echo "$numbers" | awk -F"-" '{print $2}'`
if [ -z "$end" ];then
sed -n $begin'p' $diff_file >> $tmp_file
else
if [ "$end" -lt $begin ];then
return 1
fi
sed -n $begin','$end'p' $diff_file >> $tmp_file
fi
if [ $? -ne 0 ];then
return 1
fi
done
awk -v dir1="$arg1" -v dir2="$arg2" '{
if (NR==1)
{
printf("edit %s/%s\nvertical diffsplit %s/%s\n", dir1, $0, dir2, $0)
}
else
{
printf("tabnew %s/%s\nvertical diffsplit %s/%s\n", dir1, $0, dir2, $0)
}
}' $tmp_file
}
#############################################################
# 获取diff info
#############################################################
temp_path1=$1
temp_path2=$2
#如果路径最后面没有带"/",都加上"/"
if [ ${1:0-1} != "/" ]; then
temp_path1=`echo $1/`
fi
if [ ${2:0-1} != "/" ]; then
temp_path2=`echo $2/`
fi
#调用diff命令,结果输出到diff_out
diff -rq "$temp_path1" "$temp_path2" > $tmp_dir/diff_out
#处理两个文件有不同的文件情况
grep "^Files" $tmp_dir/diff_out | sed "s%$temp_path2%#&%" | cut -d"#" -f2 | sed "s%$temp_path2/*%%" > $tmp_dir/diff_file
grep "Binary files" $tmp_dir/diff_out | sed "s%.*$1\/*\(.*\) and .*%\1%" >> $tmp_dir/diff_file
sed -i 's/ differ$//' $tmp_dir/diff_file
#判断文件是否为空
#if [ ! -s $tmp_dir/diff_file ];then
# rm -rf $tmp_dir/diff_file
#fi
#handle left folder only files
grep "Only in $temp_path1" $tmp_dir/diff_out |cut -d" " -f3- | sed 's%: %/%' > $tmp_dir/A_only_file
sed -i "s#.*$temp_path1/*##" $tmp_dir/A_only_file
grep -w "File ${temp_path1%/*}" $tmp_dir/diff_out | sed "s%\(File ${temp_path1%/*}.*\)while.*%\1%" >> $tmp_dir/A_only_file
#handle right folder only files
grep "Only in $temp_path2" $tmp_dir/diff_out |cut -d" " -f3- | sed 's%: %/%' > $tmp_dir/B_only_file
sed -i "s#.*$temp_path2/*##" $tmp_dir/B_only_file
grep -w "File ${temp_path1%/*}" $tmp_dir/diff_out |sed "s%.*while \(.*${temp_path2%/*}.*\)%\1%" >> $tmp_dir/B_only_file
#############################################################
# 根据输入标签打开用vim打开文件比较diff
#############################################################
if [[ ! -s $tmp_dir/diff_file && ! -s $tmp_dir/A_only_file && ! -s $tmp_dir/B_only_file ]];then
echo folders are the same!
myexit
fi
show_diff $tmp_dir/diff_file
while true
do
if [ -s $tmp_dir/diff_file ];then
echo -n "Please choose file number list (like this:1,3-4,5):"
else
echo "No diff files,enter 'q' to exit!"
echo -n ":"
fi
read value
if [[ "$value" = "s" ]] || [[ "$value" = "S" ]];then
show_diff $tmp_dir/diff_file
continue
elif [[ "$value" = "q" ]] || [[ "$value" = "Q" ]];then
myexit
elif [[ "$value" = "a" ]] || [ "$value" = "A" ];then
value="1-$"
fi
vim_script=`check_value $tmp_dir/diff_file "$value" 2>/dev/null`
if [ $? -ne 0 ];then
echo "invalid parameter[$value]!"
else
vim -c "$vim_script"
fi
done
基于vim插件的方式
参考https://www.vim.org/scripts/script.php?script_id=102