Bash 如何在linux中创建winmerge等价物
我的朋友最近问我如何在linux中比较两个文件夹,然后对任何不同的文本文件运行meld。我慢慢地理解了linux的理念,即将许多粒度实用程序连接在一起,我提出了以下解决方案。我的问题是,如何改进这个脚本。似乎有相当多的冗余,我希望学习编写unix脚本的更好方法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
#!/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;也看看