Answers:
该diffutils
软件包包括一个lsdiff
工具。只需将输出传递diff -u
给lsdiff:
diff -u --other-diff-options path1 path2 | lsdiff
patchutils
我的包装中(CentOS 5.x)。
要仅使用已更改文件的名称,我使用以下命令:
diff -r dirt1 dir2 --brief | sed 's/^Only in \([^:]*\): /\1\//' | sed 's/^Files \(.*\) and .* differ/\1/'
如果需要排除某些文件作为目标文件或库文件,则可以使用:
diff -r dirt1 dir2 --brief --exclude "*.o" --exclude "*.a" | sed 's/^Only in \([^:]*\): /\1\//' | sed 's/^Files \(.*\) and .* differ/\1/'
要以编程方式创建新文件或修改文件的列表,我能想到的最佳解决方案是使用rsync,sort和uniq:
(rsync -rcn --out-format="%n" old/ new/ && rsync -rcn --out-format="%n" new/ old/) | sort | uniq
让我用这个例子来解释:我们想比较两个dokuwiki版本,以查看哪些文件已更改以及哪些文件是新创建的。
我们使用wget提取焦油并将其提取到目录old/
和new/
:
wget http://download.dokuwiki.org/src/dokuwiki/dokuwiki-2014-09-29d.tgz
wget http://download.dokuwiki.org/src/dokuwiki/dokuwiki-2014-09-29.tgz
mkdir old && tar xzf dokuwiki-2014-09-29.tgz -C old --strip-components=1
mkdir new && tar xzf dokuwiki-2014-09-29d.tgz -C new --strip-components=1
以一种方式运行rsync可能会丢失新创建的文件,因为rsync和diff的比较如下所示:
rsync -rcn --out-format="%n" old/ new/
产生以下输出:
VERSION
doku.php
conf/mime.conf
inc/auth.php
inc/lang/no/lang.php
lib/plugins/acl/remote.php
lib/plugins/authplain/auth.php
lib/plugins/usermanager/admin.php
仅在一个方向上运行rsync会丢失新创建的文件,而在另一方向上运行rsync则会丢失已删除的文件,请比较diff的输出:
diff -qr old/ new/
产生以下输出:
Files old/VERSION and new/VERSION differ
Files old/conf/mime.conf and new/conf/mime.conf differ
Only in new/data/pages: playground
Files old/doku.php and new/doku.php differ
Files old/inc/auth.php and new/inc/auth.php differ
Files old/inc/lang/no/lang.php and new/inc/lang/no/lang.php differ
Files old/lib/plugins/acl/remote.php and new/lib/plugins/acl/remote.php differ
Files old/lib/plugins/authplain/auth.php and new/lib/plugins/authplain/auth.php differ
Files old/lib/plugins/usermanager/admin.php and new/lib/plugins/usermanager/admin.php differ
两种方式都运行rsync并对输出进行排序以除去重复项,这表明该目录data/pages/playground/
和文件data/pages/playground/playground.txt
最初是丢失的:
(rsync -rcn --out-format="%n" old/ new/ && rsync -rcn --out-format="%n" new/ old/) | sort | uniq
产生以下输出:
VERSION
conf/mime.conf
data/pages/playground/
data/pages/playground/playground.txt
doku.php
inc/auth.php
inc/lang/no/lang.php
lib/plugins/acl/remote.php
lib/plugins/authplain/auth.php
lib/plugins/usermanager/admin.php
rsync
使用以下参数运行:
-r
“递归到目录”, -c
还比较相同大小的文件,并且仅“基于校验和而不是mod-time&size”跳过文件, -n
“不进行任何更改即可进行试运行”,并且--out-format="%n"
“使用指定的格式输出更新”,此处仅文件名是“%n”rsync
使用组合两个方向的输出(文件列表)并进行排序sort
,然后通过使用删除所有重复项来压缩此排序列表。uniq
这可能会达到目的:
compare_dirs()
{
# Shows which files and directories exist in one directory but not both
if [ $# -ne 2 ]
then
echo "Usage: compare_dirs dir1 dir2" >&2
return 2
fi
for path
do
if [ ! -d "$path" ]
then
echo "Not a directory: $path" >&2
return 1
fi
done
comm -3 \
<(cd -- "$1" && find . -printf '%P\0' | sort -z | quote_shell) \
<(cd -- "$2" && find . -printf '%P\0' | sort -z | quote_shell)
}
通常,您将文件放入某种版本控制系统中,例如SubVersion或git,因为这些文件可以开箱即用。
但是您可以在dir1上执行一个带for循环的快速脚本,然后将每个文件与dir2中的文件进行比较。for循环可以查看diff的退出代码,以了解文件是否不同。
也许是这样的:
for f in `(cd dir1 ; find .)`
do
diff $f ../dir2/$f
if [ "$?" == "0" ]
then
echo same
else
echo diff: $f
fi
done
注意:脚本未经测试,因此上面的示例是“ bash启发式伪代码” ...
让我们再走一遍,但使用git
创建一些示例文件进行播放
mkdir -p dir1/test1/test11
mkdir -p dir1/test1/test12
mkdir -p dir1/test1/test13
echo "Test1" >> dir1/test1/test11/t1.txt
echo "Test2" >> dir1/test1/test12/t2.txt
echo "Test3" >> dir1/test1/test13/t3.txt
#And a dir to work in
mkdir gitdir
然后输入目录并导入dir1
cd gitdir/
git init .
cp -r ../dir1/* .
git add .
git commit -m 'dir1'
出去修改dir1(这样就成为您的dir2)
cd ..
echo "Test2" > dir1/test1/test11/t1.txt
然后进入git dir并导入新目录
cd gitdir/
cp -r ../dir1/* .
现在询问git有什么变化(使用status命令)
git status -s
输出是包含更改的列表,如下所示:
M test1/test11/t1.txt
这类似于rsync:显示何时覆盖目标位置上的较新文件(稍后询问,尽管不是重复的)。
如问题所示,“ diff -q -r”可能需要一些处理才有用。该问题未指定输出形式;答案给出了不同类型的报告。
rsync
是实现这一目的的一个有用工具,因为它是很多的速度比diff
。但是,@ nils建议的解决方案比旧/新目录树之间的实际差异更为冗长(并列出了更多文件)。例如,将其与我为该答案编写的脚本进行比较,并在相同的数据上运行,
为了diff
正确地考虑新文件,您还需要该-N
选项(我在任何建议的答案中都没有看到)。但是,由于它要慢得多(数量级)rsync
,因此提高后者的输出似乎是路要走。
进一步阅读
我一直偏爱sha1sum(甚至是md5sum;在这种情况下,这是相当安全的)。
find . -type f -print0 | xargs -0 md5sum | sort -k2 > /tmp/before
# don't miss the "sort" in there; it's important
# (later)
find . -type f -print0 | xargs -0 md5sum | sort -k2 > /tmp/after
vimdiff /tmp/before /tmp/after
# or whatever diff tool you like, even "diff -u"
有时(例如,如果您有太多文件被重命名或移动),请在第一个字段上进行排序,然后执行diff可能会有所帮助,但在大多数情况下,这已经足够了。
请注意,与其他一些方法相比,它的优点是您不需要保留“ before”文件的副本。仅md5sum输出文件。