Bash 如何在linux中创建winmerge等价物

Bash 如何在linux中创建winmerge等价物,bash,Bash,我的朋友最近问我如何在linux中比较两个文件夹,然后对任何不同的文本文件运行meld。我慢慢地理解了linux的理念,即将许多粒度实用程序连接在一起,我提出了以下解决方案。我的问题是,如何改进这个脚本。似乎有相当多的冗余,我希望学习编写unix脚本的更好方法 #!/bin/bash dir1=$1 dir2=$2 # show files that are different only cmd="diff -rq $dir1 $dir2" eval $cmd # print this ou

我的朋友最近问我如何在linux中比较两个文件夹,然后对任何不同的文本文件运行meld。我慢慢地理解了linux的理念,即将许多粒度实用程序连接在一起,我提出了以下解决方案。我的问题是,如何改进这个脚本。似乎有相当多的冗余,我希望学习编写unix脚本的更好方法

#!/bin/bash

dir1=$1
dir2=$2

# show files that are different only
cmd="diff -rq $dir1 $dir2"
eval $cmd # print this out to the user too
filenames_str=`$cmd`

# remove lines that represent only one file, keep lines that have
# files in both dirs, but are just different
tmp1=`echo "$filenames_str" | sed -n '/ differ$/p'` 

# grab just the first filename for the lines of output
tmp2=`echo "$tmp1" | awk '{ print $2 }'`

# convert newlines sep to space
fs=$(echo "$tmp2") 

# convert string to array
fa=($fs) 

for file in "${fa[@]}"
do
    # drop first directory in path to get relative filename
    rel=`echo $file | sed "s#${dir1}/##"`

    # determine the type of file
    file_type=`file -i $file | awk '{print $2}' | awk -F"/" '{print $1}'`

    # if it's a text file send it to meld
    if [ $file_type == "text" ]
    then
        # throw out error messages with &> /dev/null
        meld $dir1/$rel $dir2/$rel &> /dev/null
    fi 
done

请保留/提高答案的可读性。一个简短但难以理解的答案不能作为答案。

这是一个老问题,但让我们做一点工作只是为了好玩,而不考虑最终目标,可能是SCM,也不考虑已经以更好的方式实现这一目标的工具。让我们专注于脚本本身

在OP的脚本中,bash内部使用sed和awk等工具进行了大量字符串处理,有时在同一命令行中或在每个文件执行n次的循环中进行多次处理

没关系,但有必要记住:

每次脚本调用这些程序中的任何一个,都会在操作系统中创建一个新的进程,这在时间和资源上都很昂贵。因此,调用的程序越少,正在执行的脚本的性能越好:

仅打印给用户的差异为2乘以1 sed 1次处理差异结果,每个文件1次 awk处理sed结果1次,每个文件处理结果2次 每个文件1次存档 这不适用于echo、read、test和其他bash内置命令,因此不执行任何外部程序

meld是将文件显示给用户的最后一个命令,因此它不起作用。 即使使用内置命令,重定向管道也有成本,因为shell必须创建管道、复制句柄,甚至可能创建shell本身就是一个进程的分叉。所以再说一遍:越少越好。 diff命令的消息依赖于语言环境,因此如果系统不是英语,整个脚本将无法工作。 考虑到这一点,让我们清理一下原始脚本,保留OP的逻辑:

#!/bin/bash

dir1=$1
dir2=$2

# Set english as current language
LANG=en_US.UTF-8
# (1) show files that are different only
diff -rq $dir1 $dir2 | 
    # (2) remove lines that represent only one file, keep lines that have
    # files in both dirs, but are just different, delete all but left filename
    sed '/ differ$/!d; s/^Files //; s/ and .*//' |
    # (3) determine the type of file
    file -i -f - | 
    # (4) for each file
    while IFS=":" read file file_type
    do
        # (5) drop first directory in path to get relative filename
        rel=${file#$dir1}
        # (6) if it's a text file send it to meld
        if [[ "$file_type" =~ "text/" ]]
        then
            # throw out error messages with &> /dev/null
            meld ${dir1}${rel} ${dir2}${rel} &> /dev/null
        fi 
    done
稍微解释一下:

唯一的命令链cmd1 | cmd2 |。。。其中,前一个的输出stdout是下一个的输入stdin。 只执行一次sed,执行3个操作,用;在差异输出中: 删除以different结尾的行 删除剩余行开头的文件 删除剩余行的结尾处和结尾处 执行命令文件一次,以处理stdin选项-f中的文件列表- 使用while bash语句读取两个值,每行stdin之间用:分隔。 使用bash变量替换从变量中提取文件名 使用bash测试将文件类型与正则表达式进行比较 为了清楚起见,我没有考虑到文件名和目录名可能有空格。在这种情况下,两个脚本都将失败。为了避免这种情况,有必要将对file/dir name变量的任何引用用双引号括起来


我没有使用awk,因为它足够强大,可以替代几乎整个脚本-

你知道xmerge?不是,但当我查找它时,有两件事我不喜欢:不是FOSS,似乎使用它自己的SCM,但也许它在一个简单的文件系统上工作得很好?上述解决方案是100%自由/开源软件。似乎xmerge/xdiff也有一些“好处”,比如在比较中可能略优于meld?更好地支持移动块?就我个人而言,我一直对meld tho感到满意。不过看起来也很滑很漂亮,谢谢分享!对不起,实际上我指的是xxdiff;也看看