使用bash以递归方式查找具有相同名称但实际上不同的文件的最佳方法?

时间:2014-06-13 00:17:49

标签: linux bash unix

我在嵌套文件结构中有大约15000个图像,其名称为SKUS。我需要确保没有相同SKU的文件实际上是不同的文件。

例如,如果我有两个或多个名为MYSKU.jpg的文件,我需要确保它们之间没有任何不同。

在bash命令中执行此操作的最佳方式是什么?

3 个答案:

答案 0 :(得分:3)

我不想完全为你解决任务,但这里有一些你可以尝试和整合的有用成分:

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校验和,以验证它们是否真的不同,也是完整路径,以便执行mvrmln等行为。< / p>

答案 1 :(得分:1)

我们的想法是扫描所有文件的目录,并根据md5校验和检查哪个文件名相同但内容不同

#!/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

答案 2 :(得分:0)

以下是我将如何用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()和校验和你不需要的那些,所以这种方法被调整为将数据集缩小到重复文件名,然后只对内容进行哈希处理。正如其他人之前所说的那样,你可以通过在散列之前检查文件大小来更快地实现这一点,但是我会让那部分未完成。