楚客
  • Home
  • Categories
  • Tags
  • Archives

vim比较目录diff

Contents

  • vim比较文件diff
  • vim在新标签比较文件diff
  • vim批量执行命令
  • 最终实现

虽然现在有很多图形界面的diff工具,但对于有命令行情节的人来说,当飞快的在terminal下敲击键盘时,总不希望再拿鼠标去点击其它地方,况且有时候图形界面占用资源多,我的MBA就经常启动diffmerge时卡住,但vimdiff又只能在一个标签里比较一组文件的diff,如果想比较两个目录下文件的diff,它就显得无能为力了

假设我们要实现一个工具叫diffdir,先让我们脑洞打开设想一下它应该是怎样的

  1. 我希望能列出两个目录下文件名相同但内容不同的所有文件,并进行编号
  2. 我希望通过选择编号,打开需要比较diff的文件
  3. 如果想比较多组文件的diff,我希望每个vim标签打开一组文件比较
  4. 最好能过滤掉非文本文件,因为我不希望用vim打开一对二进制乱码
  5. 最好还能有交互,我可以选择只查看我感兴趣的文件,而不是一次打开所有文件的diff,当退出vim时我还可以继续选择

假设有两个目录分别是A和B,目录结构如下

A
├── file1
├── file2
└── file3

B和A目录结构以及对应文件名都相同,其中file1和file2的内容不同,file3内容相同,那么当我们运行diffdir A B时,它应该是这样的界面

当我们选择编号1时,vim会打开一个标签对比两个目录下file1的差异
当我们选择1,2或1-2时,vim会打开两个标签分别比较file1和file2的差异
由于这个例子有diff的文件数量较少,我们还可以选择a一次打开所有文件的diff
如果diff文件个数较多,我们可以分批打开,并且当我们退出vim后还可以继续选择

接下来是实现

vim比较文件diff¶

我们都知道vimdiff的用法,其实vimdiff A/file1 B/file1等价于vim -d A/file1 B/file2,又或者更原始一点,我们可以分两步来比较两个文件的diff

  1. 执行vim A/file1
  2. 在normal模式下输入:vertical diffsplit B/file1

虽然人们不会用这么麻烦的命令去比价文件的diff,但往往最基本的命令反而能组合出更多的功能,就像搭积木一样,我们只需要几个基本的形状,就可以通过自己的想象搭建多彩的世界,而vim的这些基本命令就像积木一样,我们要做的是利于好这些积木

vim在新标签比较文件diff¶

假设我们已经用上面的命令打开了vim并比较file1的diff,如果我们希望新建一个标签来比较file2的diff呢,还是要用到基本的ex命令

  1. 在normal模式下执行:tabnew A/file2
  2. 在normal模式下执行:vertical diffsplit B/file1

vim批量执行命令¶

以上两个示例就是我们需要的积木,有了积木,我们就可以组合出强大的命令,现在要做的是同时打开两组文件的diff,并且每个标签一组diff

通过查看vim帮助我们发现vim有如下两个参数

-c <command>         加载第一个文件后执行 <command>
-S <session>         加载第一个文件后执行文件 <session>

这两个参数都可以让vim启动时执行一些命令,其中-c是从参数读取命令,-S是从文件读取命令,于是我们就可以将需要执行的命令存入文件,启动vim时通过-S参数加载该文件,就能达到我们批量执行命令的目的。假设我们需要打开两个标签,分别比较A,B目录下file1和file2的diff,事先创建vim.script如下(文件名随意,最好采用绝对路径,以免受到vim配置里autochdir的影响)

edit A/file1
vertical diffsplit B/file1
tabnew A/file2
vertical diffsplit B/file2

然后执行vim -S vim.script,看看是否如你所愿,打开了两个标签,分别比较file1和file2的diff。注意,为了

最终实现¶

既然有了这些积木,那我们就可以灵活的根据需要编写脚本实现我们的需求,下面是我最终的实现,也可以在github上查看源码

https://github.com/handy1989/vim/blob/master/diffdir

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/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=`greadlink -f $1`
arg2=`greadlink -f $2`

tmp_dir=/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
    md5 $file | awk -F"=" '{print $2}'
}

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_ony_file ];then
        awk '{printf("    [%2d] %-50s\n", NR, $1)}' $tmp_dir/A_ony_file
        python -c 'print "-"*100'
    fi
    awk '{printf("    [%2d] %-50s  %-50s\n", NR, $1, $1)}' $diff_file
    echo "(s):show diff files (a):open all diff files (q):exit"
    echo
}

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 file in `find $arg1 | grep -v "/\." | grep -v "^\."`
do
    file_relative_name=${file#$arg1/}
    file $file | grep -Eq "text"
    if [ $? -ne 0 ];then
        continue
    fi
    if [ -f $arg2/$file_relative_name ];then
        file $arg2/$file_relative_name | grep -Eq "text"
        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_ony_file
    fi
done

#############################################################
# 根据输入标签打开用vim打开文件比较diff
#############################################################
if [ ! -f $tmp_dir/diff_file ];then
    exit
fi

show_diff $tmp_dir/diff_file
while true
do
    echo -n "Please choose file number list (like this:1,3-4,5):"
    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自动更新tag
  • 一行Python代码——单词逆转 »
Published 2015- 4-27
Category Skill
Tags
  • vim 4
  • Powered by Pelican. Theme: Elegant by Talha Mansoor