1. 程式人生 > >利用Vim進行資料夾對比的三種方式

利用Vim進行資料夾對比的三種方式

前言

最近經常使用vim, 心血來潮想研究了一下如何用Vim進行程式碼merge. 在Windows下有Beyond Compare和WinMerge等軟體,可以比較兩個目錄結構及檔案內容的異同,並以圖形介面的形式呈現給使用者。Vim有的vimdiff可以進行檔案內容的對比和merge操作,但是很遺憾的是隻針對單個檔案比較,資料夾的比較就無能為力了。

在網上這方面的介紹不多,只搜到https://blog.csdn.net/littlewhite1989/article/details/45312081。該文章介紹了一種linux指令碼結合Vim進行檔案對比的方法,但是原指令碼有些地方沒有考慮周全,所以對其進行了一些優化。比如檔名或者資料夾名裡面有空格等特殊符的情況有異常,只在資料夾B裡面的檔案或者目錄漏掉了。

在區分兩個檔案是否相同時,原文是用md5sum命令實現,在子檔案較多時處理時間比較長。這部分可以用linux自帶的diff命令進行區分,據此進行了指令碼優化。

基於md5sum的資料夾對比

基本思想參考https://blog.csdn.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 vim dirdiff外掛