bash 递归比较目录的Shell脚本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6765054/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:24:36  来源:igfitidea点击:

Shell script to compare directories recursively

bashshelldiff

提问by Brandon

I have a file server backup on an external hard drive a few months old for a file server that went down since then. Most of the data was recovered onto a temporary file server thats been in use since then but there are some inconsistencies.

我在几个月前的外部硬盘驱动器上有一个文件服务器备份,用于从那时起停机的文件服务器。大多数数据被恢复到一个临时文件服务器上,该服务器从那时起一直在使用,但存在一些不一致之处。

I am going to mount the external and rsync it with the current data to it but first I need to establish files that have gotten updated on the newer copy.

我将挂载外部并将其与当前数据同步到它,但首先我需要建立已在较新副本上更新的文件。

I can do diff -r -q /old/ /new/ to obtain this, I am trying to get better at scripting so I am trying to write something that will rename the old file to filename.old whenever diff returns a difference.

我可以做 diff -r -q /old/ /new/ 来获得这个,我试图在脚本方面做得更好,所以我试图写一些东西,只要 diff 返回差异,就会将旧文件重命名为 filename.old。

回答by Manny D

So after checking, I wasn't able to find an option in diffto only output the filename differences so we'll just work with what diff outputs.

因此,在检查之后,我无法找到diff仅输出文件名差异的选项,因此我们将只使用差异输出。

If difffinds files that differ, the output is something like this:

如果diff找到不同的文件,输出是这样的:

Files old/file and new/file differ

Since all your bash script would be doing is renaming the changed file from the old directory, we want to extract old/filefrom this output. Let's start by only displaying lines like Files...differ(as other lines may be produced):

由于您的所有 bash 脚本都将重命名旧目录中已更改的文件,因此我们希望old/file从此输出中提取。让我们从只显示像这样的行开始Files...differ(因为可能会产生其他行):

diff -rq old/ new/ | grep "^Files.*differ$"

Now you'll only get lines like the one shown before. Next step is getting the filename. You can do this with awkby adding something like awk '{print $2}'as another pipe but if the filename itself contains spaces, awk will break up that as two separate strings. We'll use sed instead:

现在,您将只会得到像之前显示的那样的行。下一步是获取文件名。您可以awk通过添加类似awk '{print $2}'另一个管道的东西来做到这一点,但如果文件名本身包含空格,awk 会将其分解为两个单独的字符串。我们将使用 sed 代替:

diff -rq old/ new/ | grep "^Files.*differ$" | sed 's/^Files \(.*\) and .* differ$//'

Now this will produce a list of files that have changed in the old directory. Using a simple for loop, you can now rename each of the files:

现在这将生成旧目录中已更改的文件列表。使用简单的 for 循环,您现在可以重命名每个文件:

for old_file in `diff -rq old/ new/ | grep "^Files.*differ$" | sed 's/^Files \(.*\) and .* differ$//'`
do
   mv $old_file $old_file.old
done

And that's it!

就是这样!

edit: actually, that's not all. This loop still fails on files with spaces so let's muck with it a bit. forwill try to produce a list separated by a space by default. Let's change this to use newlines instead:

编辑:实际上,这还不是全部。这个循环在带有空格的文件上仍然失败,所以让我们稍微处理一下。for默认情况下,将尝试生成一个由空格分隔的列表。让我们将其更改为使用换行符:

OLD_IFS=$IFS
# The extra space after is crucial
IFS=\

for old_file in `diff -rq old/ new/ | grep "^Files.*differ$" | sed 's/^Files \(.*\) and .* differ$//'`
do
   mv $old_file $old_file.old
done
IFS=$OLD_IFS

This temporarily replaces bash's default separator ($IFS) to a newline and puts it back after it's done with the loop so you don't split by space.

这会暂时将 bash 的默认分隔符 ( $IFS)替换为换行符,并在循环完成后将其放回原处,这样您就不会被空格分隔。

回答by Jonathan Leffler

There used to be a program dircmplurking around systems. If you have it, use it.

曾经有一个程序dircmp潜伏在系统周围。如果你有它,请使用它。

If you don't have and can't find it, then I have a version of it with minor extensions that you can use (see my profile for contact information).

如果您没有并且找不到它,那么我有一个带有小扩展的版本,您可以使用它(有关联系信息,请参阅我的个人资料)。

Example output:

示例输出:

Files in ifxchkpath-4.12 only and in ifxchkpath-5.15 only

./Makefile                        ./absname.1
./program.mk                      ./absname.c
                                  ./chk.servers.sh
                                  ./chunklist.sh
                                  ./clnpath.c
                                  ./clnpath.h
                                  ./dirname.c
                                  ./enable.uids.sh
                                  ./errhelp.c
[...]
                                  ./lpt.pl
                                  ./makefile
                                  ./nvstrcpy.c
                                  ./realpath.c
                                  ./realpathtest.sh
                                  ./stderr.c
                                  ./stderr.h
                                  ./symlinks.tgz
                                  ./testids.mk
                                  ./test.linkpath.pl
                                  ./test.lpt.sh
                                  ./test.onsecurity.sh
                                  ./tokenise.c

Comparison of files in ifxchkpath-4.12 and ifxchkpath-5.15

directory           .
different           ./chk.ifxchkpath.sh
different           ./ifxchkpath.c
same                ./test.ifxchkpath.pl