我运行了一个脚本,它检查多个目录,并将它们与其他地方相同目录的扩展tarball进行比较。
我使用的是diff -r -q
,我希望当diff
在递归运行中发现任何差异时,它将停止运行,而不是在同一运行中遍历更多目录。
感谢所有的帮助!
谢谢
@bazzargh我确实按照你的建议或像这样试过了。
for file in $(find $dir1 -type f);
do if [[ $(diff -q $file ${file/#$dir1/$dir2}) ]];
then echo differs: $file > /tmp/$runid.tmp 2>&1; break;
else echo same: $file > /dev/null; fi; done
但这只适用于同时存在于两个目录中的文件。如果有一个文件不见了,我就不会得到相关信息。此外,我正在使用的目录有超过300000个文件,所以为每个文件先做find
,然后再做diff
似乎有点开销。
我希望像这样的东西能够使用elif
语句,该语句检查$runid.tmp
是否包含数据,如果包含数据则中断。我在第一个if
语句之后添加了2>
,所以stderr
被发送到$runid.tmp
文件。
for file in $(find $dir1 -type f);
do if [[ $(diff -q $file ${file/#$dir1/$dir2}) ]] 2> /tmp/$runid.tmp;
then echo differs: $file > /tmp/$runid.tmp 2>&1; break;
elif [[ -s /tmp/$runid.tmp ]];
then echo differs: $file >> /tmp/$runid.tmp 2>&1; break;
else echo same: $file > /dev/null; fi; done
这行得通吗?
当文件不同时,您可以使用"find"和break对文件进行循环。例如,对于dirs foo,bar:
for file in $(find foo -type f); do if [[ $(diff -q $file ${file/#foo/bar}) ]]; then echo differs: $file; break; else echo same: $file; fi; done
注意,这将无法检测"bar"中是否有"foo"中不存在的目录。
编辑补充:我刚刚意识到我忽略了真正显而易见的解决方案:
diff -rq foo bar | head -n1
它不是"diff",但使用"awk"可以比较两个(或多个)文件,然后在它们有不同行时退出。
试试这样的东西(对不起,有点粗糙)
awk '{ h[$0] = ! h[$0] } END { for (k in h) if (h[k]) exit }' file1 file2
消息来源就在这里。
edit:当两个文件具有相同的行时,要打破循环,您可能必须在awk中执行循环。请参见此处。
您可以尝试以下操作:
#!/usr/bin/env bash
# Determine directories to compare
d1='./someDir1'
d2='./someDir2'
# Loop over the file lists and diff corresponding files
while IFS= read -r line; do
# Split the 3-column `comm` output into indiv. variables.
lineNoTabs=${line//$'t'}
numTabs=$(( ${#line} - ${#lineNoTabs} ))
d1Only='' d2Only='' common=''
case $numTabs in
0)
d1Only=$lineNoTabs
;;
1)
d2Only=$lineNoTabs
;;
*)
common=$lineNoTabs
;;
esac
# If a file exists in both directories, compare them,
# and exit if they differ, continue otherwise
if [[ -n $common ]]; then
diff -q "$d1/$common" "$d2/$common" || {
echo "EXITING: Diff found: '$common'" 1>&2;
exit 1; }
# Deal with files unique to either directory.
elif [[ -n $d1Only ]]; then # fie
echo "File '$d1Only' only in '$d1'."
else # implies: if [[ -n $d2Only ]]; then
echo "File '$d2Only' only in '$d2."
fi
# Note: The `comm` command below is CASE-SENSITIVE, which means:
# - The input directories must be specified case-exact.
# To change that, add `I` after the last `|` in _both_ `sed commands`.
# - The paths and names of the files diffed must match in case too.
# To change that, insert `| tr '[:upper:]' '[:lower:]' before _both_
# `sort commands.
done < <(comm
<(find "$d1" -type f | sed 's|'"$d1/"'||' | sort)
<(find "$d2" -type f | sed 's|'"$d2/"'||' | sort))
该方法基于为每个输入目录构建一个包含相对路径(使用sed
删除根路径)的文件列表(使用CCD11),对列表进行排序,并将其与comm
进行比较,CCD_13产生3列、以制表符分隔的输出,以指示哪些行(以及文件)对第一列表是唯一的,哪些行对第二列表是唯一,以及它们有哪些共同点。
因此,第三列中的值可以是diff
ed,如果它们不相同,则可以采取行动。此外,第1列和第2列的值可用于根据唯一文件执行操作。
有必要将comm
输出的3列值复杂地拆分为单个变量,因为:
read
将按顺序将多个选项卡视为单个分隔符- CCD_ 17输出数量可变的选项卡;例如,如果只有第1列的值,则根本不输出选项卡
多亏了@bazzargh,我找到了解决方案。
我在脚本中使用了这段代码,现在它运行得很好。
for file in $(find ${intfolder} -type f);
do if [[ $(diff -q $file ${file/#${intfolder}/${EXPANDEDROOT}/${runid}/$(basename ${intfolder})}) ]] 2> ${resultfile}.tmp;
then echo differs: $file > ${resultfile}.tmp 2>&1; break;
elif [[ -s ${resultfile}.tmp ]];
then echo differs: $file >> ${resultfile}.tmp 2>&1; break;
else echo same: $file > /dev/null;
fi; done
谢谢!