我有大约15000张图片,文件结构嵌套,它们的名称是SKU。我需要确保没有具有相同SKU但实际上不同的文件。
例如,如果我有两个或更多名为
在bash命令中,最好的方法是什么?
例如,如果我有两个或更多名为
MYSKU.jpg
的文件,则需要确保它们之间没有任何不同。在bash命令中,最好的方法是什么?
MYSKU.jpg
的文件,则需要确保它们之间没有任何不同。我不想完全替你解决这个任务,但是以下是一些有用的要素,你可以尝试并整合:
find /path -type f # gives you a list of all files in /path
for f in $(find /path -type f -name '*.jpg'); do
...
done
base=$(basename $f)
full_path=$f
hash=$(echo $f | md5sum | awk '{print $1}')
现在,您可以将此信息存储在文件的三列中,以便每行都包含查找重复文件所需的所有信息。
由于您没有解释如何处理重复项,这里只是建议如何发现它们。然后由您决定如何处理它们。
给定上面获取的列表,您可以存储其中两个副本:一个按basename排序,另一个按basename 排除重复项排序:
sort -k2 list.txt | column -t > list.sorted.txt
sort -k2 -u list.txt | column -t > list.sorted.uniq.txt
假设基础名称位于第二列
现在运行
diff list.sorted.txt list.sorted.uniq.txt
查看具有相同名称的文件。现在,您可以从每一行中提取MD5校验和来验证它们是否真正不同,并提取完整路径以执行一些操作,例如mv
、rm
、ln
等。
for f in $(find …)
迭代文件不是最佳实践。首先,如果任何找到的路径包含空格,它将会中断。另外,它实际上比使用 find
的 -exec
或使用 shell 自己的递归 globbing 慢得多。(许多现代 shell 支持递归 glob,包括 bash 4。) - kojirofind
命令,那么请使用 while
而不是 for
:find . -type f -name '*.jpg' -print0 | while read -d '' -r f; do
(或者使用进程替换代替管道)。此外,如果你想按第二个字段排序,那么必须使用 sort -k2,2
。 - mklement0#!/bin/bash
# directory to scan
scan_dir=$1
[ ! -d "$1" ] && echo "Usage $0 <scan dir>" && exit 1
# Associative array to save hash table
declare -A HASH_TABLE
# Associative array of full path of items
declare -A FULL_PATH
for item in $( find $scan_dir -type f ) ; do
file=$(basename $item)
md5=$(md5sum $item | cut -f1 -d\ )
if [ -z "${HASH_TABLE[$file]}" ] ; then
HASH_TABLE[$file]=$md5
FULL_PATH[$file]=$item
else
if [ "${HASH_TABLE[$file]}" != "$md5" ] ; then
echo "differ $item from ${FULL_PATH[$file]}"
fi
fi
done
使用方法(假设您将脚本文件命名为scan_dir.sh
):
$ ./scan_dir.sh /path/to/you/directory
for
来解析命令输出(请参见@Pavel答案或http://mywiki.wooledge.org/ParsingLs进行讨论和替代方案),(b)双引号所有`$scan_dir`和`$item`引用,(c)将用法信息发送到_stderr_,因为您正在报告一个_error_。 - mklement0以下是我将如何使用bash 4来解决它的方法:
#!/usr/local/bin/bash -vx
#!/usr/local/bin/bash -vx
shopt -s globstar # turn on recursive globbing
shopt -s nullglob # hide globs that don't match anything
shopt -s nocaseglob # match globs regardless of capitalization
images=( **/*.{gif,jpeg,jpg,png} ) # all the image files
declare -A homonyms # associative array of like named files
for i in "${!images[@]}"; do # iterate over indices
base=${images[i]##*/} # file name without path
homonyms["$base"]+="$i " # Space delimited list of indices for this basename
done
for base in "${!homonyms[@]}"; do # distinct basenames
unset dupehashes; declare -A dupehashes # temporary var for hashes
indices=( ${homonyms["$base"]} ) # omit quotes to allow expansion of space-delimited integers
(( ${#indices[@]} > 1 )) || continue # ignore unique names
for i in "${indices[@]}"; do
dupehashes[$(md5 < "${images[i]}")]+="$i "
done
(( ${#dupehashes[@]} > 1 )) || continue # ignore if same hash
echo
printf 'The following files have different hashes: '
for h in "${!dupehashes[@]}"; do
for i in ${dupehashes[$h]}; do # omit quotes to expand space-delimited integer list
printf '%s %s\n' "$h" "${images[i]}"
done
done
done
我知道上面看起来很多,但是我认为如果有15k张图片,你真的想避免打开(open()
)和校验那些不必要的图片,所以这种方法是针对将数据集缩小到重复文件名,然后仅对其进行哈希处理。正如其他人之前所说,你可以在哈希处理之前通过检查文件大小来使其更快,但我会留下这部分未完成。