参考这个发布根据校验和查找并删除重复文件,我想修改执行复制操作的方法,然后对目标文件进行文件完整性检查。
SOURCE = /path/to/Source
DEST = /path/to/Destination
# filecksums containing the md5 of the copied files
declare -A filecksums
for file in "$@"
do
[[ -f "$file" ]] || continue
# Generate the checksum
cksum=$(cksum <"$file" | tr ' ' _)
# Can an exact duplicate be found in the destination directory?
if [[ -n "${filecksums[$cksum]}" ]] && [[ "${filecksums[$cksum]}" != "$file" ]]
then
rm -f "$file"
else
echo " '$file' is not in '$DEST'" >&2
fi
done
我想使用 md5 校验和比较的结果,rm -f
仅当校验和相等时才允许源文件。如果有差异,我想回显结果并转义。rsync
可能是另一种选择,但我认为强制本地-本地文件传输的校验和比较会遇到问题。
更新
我已经根据 @Lucas 的答案研究了使用 rsync 。似乎有一些选项可以通过检查而不是批量传输文件来更稳定地传输文件mv /data1/* /data2/
,并报告已完成的操作并在检查后删除。正如社区成员所指出的,这可能会缩小定义范围。
答案1
如果您关心文件并且不想搞乱,那么第一次尝试实现这样的事情可能会很困难。因此,这里有一些在 bash 中编写完整脚本的替代方法。这些或多或少复杂的命令行(oneliners)可能对您的情况有所帮助。
你的问题有一个不确定性:你想比较吗每个源文件中包含每一个文件位于 dest 中还是仅包含那些具有“匹配”文件名的文件? (这将是/path/to/src/a
与/path/to/dest/a
与/path/to/src/b
比较,/path/to/dest/b
但不是/path/to/src/a
与/path/to/dest/b
等等)
我假设您只想比较具有匹配路径的文件!
第一个想法:diff
旧的好diff
可以递归比较目录。还可以使用该-q
选项来查看哪些文件不同,哪些文件不同如何他们不同。
diff -r -q /path/to/source /path/to/dest
缺点
- 这可能需要一个长的时间取决于您的硬盘大小。
- 这不会删除旧文件。
- 输出不容易解析
优点
- 这不会删除任何文件:)
因此,在您手动/目视确认您关心的任何文件没有差异后,您必须手动删除带有rm -rf /path/to/source
.
第二个想法:(rsync
编辑:这可能是现在最好的)
rsync
是所有复制命令行工具的大师(在我看来;)。正如对您的问题的评论中提到的,它有一个--checksum
选项,但它还有大量其他选项。它可以将文件从本地传输到远程、从远程到本地、从本地到本地。我认为最重要的功能之一是,如果您提供正确的选项,您可以中止并重新启动命令(再次执行相同的命令行),并且它将从原来的位置继续!
出于您的目的,以下选项可能会很有趣:
-v
:详细,显示发生的情况可以多次给出,但通常一次就足够了-n
:试运行,测试东西非常重要但不要做任何事情(结合-v
)!!-c
:使用校验和来决定应该复制什么--remove-source-files
:删除成功传输的文件(@brawny84 指出,我不知道,并且在我第一次阅读的手册页中没有找到它)
因此,此命令将覆盖dest
其校验和与相应文件source
(按名称对应)不同的所有文件。
rsync -a -c -v --remove-source-files -n /path/to/source /path/to/dest
rsync -a -c -v --remove-source-files /path/to/source /path/to/dest
优点
- 与校验和一起使用
- 有试运行模式
- 实际上将复制所有丢失的文件和与源不同的文件到目标
- 可以中止并重新启动
- 如果您不想复制所有文件,有一个排除选项可以忽略 src 中的某些文件
- 可以删除传输的源文件
缺点
- ??
第三个想法:fdupes
该程序fdupes
我设计列出重复的文件。它默认检查 md5sums。
优点
- 它使用 md5 来比较文件
- 它可以
--delete
选择删除其中一个重复项
缺点
- 它比较每个文件到所有其他文件因此,如果 dest 本身内部存在重复文件,它也会列出它们
- 删除模式似乎是交互式的,您必须对每组相同的文件进行确认,这对于大型目录树可能不可行
- 非交互模式将从每组相同的文件中删除除第一个文件之外的所有文件。但我不知道第一个文件是哪个(在源文件中还是在目标文件中?)
最后一个想法:经历实际编写和调试自己的 shell 脚本的痛苦
如果必须手动完成,我会从这样的事情开始。 我没有测试这个,先尝试一下ls
,看看它是否会刹车!
#!/bin/bash
# first require that the source and dest dirs
# are given as arguments to the script.
src=${1:?Please give the source dir as first argument}
dest=${2:?Please give the destination dir as second argument}
# go to the source directory
cd "$src"
# This assumes that there are no newlines in filenames!
# first find all plain files in the current dir
# (which should be $src)
# then use xargs to hand the filenames to md5sum
# pipe the md5 sums into a subshell
# go to the dest in the subshell
# read the md5sums from stdin and use md5sum -c to check them
# After the subshell filter lines to only keep those that end in "OK"
# and at the same time remove the "OK" stuff after the file name
# use xargs to hand these file names to ls or rm.
find . -type f | \
xargs md5sum | \
( cd "$dest" && md5sum -c ) | \
sed -n 's/: OK$//p' | \
xargs ls
最后一行ls
是列出所有通过检查的文件。如果将其替换为,rm
它们将从源目录中删除( 后的当前目录cd "$src"
)。