如何在给定目录中找到重复的文件名(递归)? BASH

时间:2013-04-29 10:40:15

标签: bash duplicates filenames

我需要在给定的目录树中找到每个重复的文件名。我不知道,dir tree用户将作为脚本参数给出什么,所以我不知道目录层次结构。我试过这个:

#!/bin/sh
find -type f | while IFS= read vo
do
echo `basename "$vo"`
done

但那不是我想要的。它只找到一个重复,然后结束,即使,如果有更多的重复文件名,它也 - 它不打印整个路径(只打印文件名)和重复计数。我想做一些类似于这个命令的事情:

find DIRNAME | tr '[A-Z]' '[a-z]' | sort | uniq -c | grep -v " 1 " 

但它对我有用,不知道为什么。即使我有重复项,也不会打印任何内容。 我使用的是Xubuntu 12.04。

6 个答案:

答案 0 :(得分:12)

这是另一种解决方案(基于@ jim-mcnamara的建议)没有awk:

解决方案1 ​​

#!/bin/sh 
dirname=/path/to/directory
find $dirname -type f | sed 's_.*/__' | sort|  uniq -d| 
while read fileName
do
find $dirname -type f | grep "$fileName"
done

但是,您必须进行两次相同的搜索。如果您必须搜索大量数据,这可能会变得非常慢。将“查找”结果保存在临时文件中可能会提供更好的性能。

解决方案2(使用临时文件)

#!/bin/sh 
dirname=/path/to/directory
tempfile=myTempfileName
find $dirname -type f  > $tempfile
cat $tempfile | sed 's_.*/__' | sort |  uniq -d| 
while read fileName
do
 grep "$fileName" $tempfile
done
#rm -f tempfile

由于您可能不想在某些情况下在硬盘驱动器上编写临时文件,因此您可以选择符合您需求的方法。 两个示例都打印出文件的完整路径。

此处的奖励问题:是否可以将find命令的整个输出保存为变量列表?

答案 1 :(得分:8)

#!/bin/sh
dirname=/path/to/check
find $dirname -type f | 
while read vo
do
  echo `basename "$vo"`
done | awk '{arr[$0]++; next} END{for (i in arr){if(arr[i]>1){print i}}}  

答案 2 :(得分:6)

是的,这是一个非常古老的问题。 但所有这些循环和临时文件似乎有点麻烦。

这是我的1行答案:

find /PATH/TO/FILES -type f -printf '%p/ %f\n' | sort -k2 | uniq -f1 --all-repeated=separate

由于uniqsort,它有其局限性:

  • 文件名中没有空格(空格,制表符)(uniqsort将被解释为新字段)
  • 需要打印文件名作为空格分隔的最后一个字段(uniq不支持仅比较1 字段,并且对字段分隔符不灵活)

但由于find -printf,它的输出非常灵活,对我来说效果很好。似乎也是@yak最初尝试实现的目标。

展示您对此的一些选择:

find  /PATH/TO/FILES -type f -printf 'size: %s bytes, modified at: %t, path: %h/, file name: %f\n' | sort -k15 | uniq -f14 --all-repeated=prepend

sortuniq中还有一些选项可以忽略大小写(作为通过tr管道实现的主题开启者)。使用man uniqman sort进行查找。

答案 3 :(得分:2)

#!/bin/bash

file=`mktemp /tmp/duplicates.XXXXX` || { echo "Error creating tmp file"; exit 1; }
find $1 -type f |sort >  $file
awk -F/ '{print tolower($NF)}' $file |
        uniq -c|
        awk '$1>1 { sub(/^[[:space:]]+[[:digit:]]+[[:space:]]+/,""); print }'| 
        while read line;
                do grep -i "$line" $file;
        done

rm $file

它也适用于文件名中的空格。这是一个简单的测试(第一个参数是目录):

./duplicates.sh ./test
./test/2/INC 255286
./test/INC 255286

答案 4 :(得分:1)

一个"找到"仅命令:

lst=$( find . -type f )
echo "$lst" | rev | cut -f 1 -d/ | rev | sort -f | uniq -i | while read f; do
   names=$( echo "$lst" | grep -i -- "/$f$" )
   n=$( echo "$names" | wc -l )
   [ $n -gt 1 ] && echo -e "Duplicates found ($n):\n$names"
done

答案 5 :(得分:0)

此解决方案将一个临时文件写入临时目录,以查找找到的每个唯一文件名。在临时文件中,我写了第一个找到唯一文件名的路径,以便稍后输出。所以,我创建了更多其他发布解决方案的文件。但是,这是我能理解的。

以下是名为fndupe的脚本。

#!/bin/bash

# Create a temp directory to contain placeholder files.
tmp_dir=`mktemp -d`

# Get paths of files to test from standard input.
while read p; do
  fname=$(basename "$p")
  tmp_path=$tmp_dir/$fname
  if [[ -e $tmp_path ]]; then
    q=`cat "$tmp_path"`
    echo "duplicate: $p"
    echo "    first: $q"
  else
    echo $p > "$tmp_path" 
  fi
done

exit

以下是使用该脚本的示例。

$ find . -name '*.tif' | fndupe

以下是脚本找到重复文件名时的示例输出。

duplicate: a/b/extra/gobble.tif
    first: a/b/gobble.tif

使用Bash版本进行测试:GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)