如何返回名为duplicates的文件列表,即具有相同名称但在同一目录中存在的不同情况?
我不关心文件的内容。我只需要知道任何具有相同名称副本的文件的位置和名称。
示例重复:
/www/images/taxi.jpg
/www/images/Taxi.jpg
理想情况下,我需要从基本目录中递归搜索所有文件。在上面的示例中,它是/www/
答案 0 :(得分:38)
另一个答案很棒,但我建议不要使用“相当可怕”的perl脚本
perl -pe 's!([^/]+)$!lc $1!e'
只会小写路径的文件名部分。
编辑1:实际上整个问题可以通过以下方式解决:
find . | perl -ne 's!([^/]+)$!lc $1!e; print if 1 == $seen{$_}++'
编辑3:我找到了一个使用sed,sort和uniq的解决方案,它也会打印出重复项,但它只有在文件名中没有空格时才有效:
find . |sed 's,\(.*\)/\(.*\)$,\1/\2\t\1/\L\2,'|sort|uniq -D -f 1|cut -f 1
编辑2:这是一个较长的脚本,将打印出名称,它采用stdin上的路径列表,由find
给出。不是很优雅,但仍然:
#!/usr/bin/perl -w
use strict;
use warnings;
my %dup_series_per_dir;
while (<>) {
my ($dir, $file) = m!(.*/)?([^/]+?)$!;
push @{$dup_series_per_dir{$dir||'./'}{lc $file}}, $file;
}
for my $dir (sort keys %dup_series_per_dir) {
my @all_dup_series_in_dir = grep { @{$_} > 1 } values %{$dup_series_per_dir{$dir}};
for my $one_dup_series (@all_dup_series_in_dir) {
print "$dir\{" . join(',', sort @{$one_dup_series}) . "}\n";
}
}
答案 1 :(得分:35)
尝试:
ls -1 | tr '[A-Z]' '[a-z]' | sort | uniq -c | grep -v " 1 "
简单,真的:-)管道是不是很棒的野兽?
ls -1
为每行提供一个文件,tr '[A-Z]' '[a-z]'
将全部大写转换为小写,sort
对它们进行排序(令人惊讶的是足够),uniq -c
删除后续出现的文件重复的行同时给你一个计数,最后,grep -v " 1 "
删除那些计数为1的行。
当我在一个“重复”(我将qq
复制到qQ
)的目录中运行时,我得到:
2 qq
对于“此目录和每个子目录”版本,如果您想要特定的目录起点(ls -1
是目录,则只需将find .
替换为find DIRNAME
或DIRNAME
你想要使用的名字。)
这会返回(对我来说):
2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3
2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3/%gconf.xml
2 ./.gnome2/accels/blackjack
2 ./qq
由以下原因引起:
pax> ls -1d .gnome2/accels/[bB]* .gconf/system/gstreamer/0.10/audio/profiles/[mM]* [qQ]?
.gconf/system/gstreamer/0.10/audio/profiles/mp3
.gconf/system/gstreamer/0.10/audio/profiles/MP3
.gnome2/accels/blackjack
.gnome2/accels/Blackjack
qq
qQ
更新
实际上,在进一步反思时,tr
将小写路径的所有组件,以便
/a/b/c
/a/B/c
将被视为重复,即使它们位于不同的目录。
如果你只希望在一个目录中重复显示为匹配,你可以使用(相当怪异):
perl -ne '
chomp;
@flds = split (/\//);
$lstf = $f[-1];
$lstf =~ tr/A-Z/a-z/;
for ($i =0; $i ne $#flds; $i++) {
print "$f[$i]/";
};
print "$x\n";'
取代:
tr '[A-Z]' '[a-z]'
它的作用是仅仅小写路径名的最后部分而不是整个部分。此外,如果您只想要常规文件(没有目录,FIFO等),请使用find -type f
来限制返回的内容。
答案 2 :(得分:5)
我相信
ls | sort -f | uniq -i -d
更简单,更快,并且会得到相同的结果
答案 3 :(得分:2)
跟进mpez0的响应,以递归方式检测,只需将“ls”替换为“find”。 我看到的唯一问题是,如果这是一个重复的目录,那么这个目录中的每个文件都有1个条目。需要一些人脑来治疗这种输出。
但无论如何,你不会自动删除这些文件,不是吗?
find . | sort -f | uniq -i -d
答案 4 :(得分:2)
如果您编译deb包不包含的findsn
,这是一个名为fslint
的小命令行应用程序。
它会找到任何具有相同名称的文件,它的闪电速度很快,可以处理不同的情况。
/findsn --help
find (files) with duplicate or conflicting names.
Usage: findsn [-A -c -C] [[-r] [-f] paths(s) ...]
如果没有提供参数,则会搜索$ PATH以查找任何冗余 或冲突的文件。
-A reports all aliases (soft and hard links) to files.
If no path(s) specified then the $PATH is searched.
如果只指定了路径,则检查它们是否有重复名称 文件。您可以使用-C来限定此值以忽略此搜索中的大小写。 使用-c进行限定更具限制性,因为只有文件(或目录) 在报告的情况下,名称不同的同一目录中。 I.E. -c将标记文件和如果转移将发生冲突的目录 到不区分大小写的文件系统。请注意,如果指定-c或-C,则 没有指定当前目录的路径。
答案 5 :(得分:2)
以下是如何查找所有重复的jar文件的示例:
find . -type f -printf "%f\n" -name "*.jar" | sort -f | uniq -i -d
将*.jar
替换为您要查找的重复文件类型。
答案 6 :(得分:1)
这是一个适合我的脚本(我不是作者)。原文和讨论可以在这里找到: http://www.daemonforums.org/showthread.php?t=4661
#! /bin/sh
# find duplicated files in directory tree
# comparing by file NAME, SIZE or MD5 checksum
# --------------------------------------------
# LICENSE(s): BSD / CDDL
# --------------------------------------------
# vermaden [AT] interia [DOT] pl
# http://strony.toya.net.pl/~vermaden/links.htm
__usage() {
echo "usage: $( basename ${0} ) OPTION DIRECTORY"
echo " OPTIONS: -n check by name (fast)"
echo " -s check by size (medium)"
echo " -m check by md5 (slow)"
echo " -N same as '-n' but with delete instructions printed"
echo " -S same as '-s' but with delete instructions printed"
echo " -M same as '-m' but with delete instructions printed"
echo " EXAMPLE: $( basename ${0} ) -s /mnt"
exit 1
}
__prefix() {
case $( id -u ) in
(0) PREFIX="rm -rf" ;;
(*) case $( uname ) in
(SunOS) PREFIX="pfexec rm -rf" ;;
(*) PREFIX="sudo rm -rf" ;;
esac
;;
esac
}
__crossplatform() {
case $( uname ) in
(FreeBSD)
MD5="md5 -r"
STAT="stat -f %z"
;;
(Linux)
MD5="md5sum"
STAT="stat -c %s"
;;
(SunOS)
echo "INFO: supported systems: FreeBSD Linux"
echo
echo "Porting to Solaris/OpenSolaris"
echo " -- provide values for MD5/STAT in '$( basename ${0} ):__crossplatform()'"
echo " -- use digest(1) instead for md5 sum calculation"
echo " $ digest -a md5 file"
echo " -- pfexec(1) is already used in '$( basename ${0} ):__prefix()'"
echo
exit 1
(*)
echo "INFO: supported systems: FreeBSD Linux"
exit 1
;;
esac
}
__md5() {
__crossplatform
:> ${DUPLICATES_FILE}
DATA=$( find "${1}" -type f -exec ${MD5} {} ';' | sort -n )
echo "${DATA}" \
| awk '{print $1}' \
| uniq -c \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && continue
SUM=$( echo ${LINE} | awk '{print $2}' )
echo "${DATA}" | grep ${SUM} >> ${DUPLICATES_FILE}
done
echo "${DATA}" \
| awk '{print $1}' \
| sort -n \
| uniq -c \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && continue
SUM=$( echo ${LINE} | awk '{print $2}' )
echo "count: ${COUNT} | md5: ${SUM}"
grep ${SUM} ${DUPLICATES_FILE} \
| cut -d ' ' -f 2-10000 2> /dev/null \
| while read LINE
do
if [ -n "${PREFIX}" ]
then
echo " ${PREFIX} \"${LINE}\""
else
echo " ${LINE}"
fi
done
echo
done
rm -rf ${DUPLICATES_FILE}
}
__size() {
__crossplatform
find "${1}" -type f -exec ${STAT} {} ';' \
| sort -n \
| uniq -c \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && continue
SIZE=$( echo ${LINE} | awk '{print $2}' )
SIZE_KB=$( echo ${SIZE} / 1024 | bc )
echo "count: ${COUNT} | size: ${SIZE_KB}KB (${SIZE} bytes)"
if [ -n "${PREFIX}" ]
then
find ${1} -type f -size ${SIZE}c -exec echo " ${PREFIX} \"{}\"" ';'
else
# find ${1} -type f -size ${SIZE}c -exec echo " {} " ';' -exec du -h " {}" ';'
find ${1} -type f -size ${SIZE}c -exec echo " {} " ';'
fi
echo
done
}
__file() {
__crossplatform
find "${1}" -type f \
| xargs -n 1 basename 2> /dev/null \
| tr '[A-Z]' '[a-z]' \
| sort -n \
| uniq -c \
| sort -n -r \
| while read LINE
do
COUNT=$( echo ${LINE} | awk '{print $1}' )
[ ${COUNT} -eq 1 ] && break
FILE=$( echo ${LINE} | cut -d ' ' -f 2-10000 2> /dev/null )
echo "count: ${COUNT} | file: ${FILE}"
FILE=$( echo ${FILE} | sed -e s/'\['/'\\\['/g -e s/'\]'/'\\\]'/g )
if [ -n "${PREFIX}" ]
then
find ${1} -iname "${FILE}" -exec echo " ${PREFIX} \"{}\"" ';'
else
find ${1} -iname "${FILE}" -exec echo " {}" ';'
fi
echo
done
}
# main()
[ ${#} -ne 2 ] && __usage
[ ! -d "${2}" ] && __usage
DUPLICATES_FILE="/tmp/$( basename ${0} )_DUPLICATES_FILE.tmp"
case ${1} in
(-n) __file "${2}" ;;
(-m) __md5 "${2}" ;;
(-s) __size "${2}" ;;
(-N) __prefix; __file "${2}" ;;
(-M) __prefix; __md5 "${2}" ;;
(-S) __prefix; __size "${2}" ;;
(*) __usage ;;
esac
如果find命令不适合您,您可能需要更改它。例如
OLD : find "${1}" -type f | xargs -n 1 basename
NEW : find "${1}" -type f -printf "%f\n"
答案 7 :(得分:1)
您可以使用:
find -type f -exec readlink -m {} \; | gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}' | uniq -c
其中:
find -type f
递归打印所有文件的完整路径。
-exec readlink -m {} \;
获取文件的绝对路径
gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}'
将所有文件名替换为小写
uniq -c
唯一的路径,-c输出重复的计数。
答案 8 :(得分:0)
这一点有点晚了,但这是我的版本:
find . -type f | awk -F/ '{print $NF}' | sort -f | uniq -i -d
我们正在使用:
find
- 查找当前目录下的所有文件awk
- 删除文件名的文件路径部分sort
- 不区分大小写uniq
- 通过管道查找欺骗行为(受@ mpez0回答的启发,以及@SimonDowdles对@paxdiablo回答的评论。)
答案 9 :(得分:0)
您可以使用GNU awk检查给定目录中的重复项:
gawk 'BEGINFILE {if ((seen[tolower(FILENAME)]++)) print FILENAME; nextfile}' *
在继续阅读文件之前,它使用BEGINFILE执行某些操作。在这种情况下,它会跟踪数组seen[]
中出现的名称,这些名称的索引是小写文件的名称。
如果名称已经出现,无论如何,都会打印出来。否则,它只是跳转到下一个文件。
查看示例:
$ tree
.
├── bye.txt
├── hello.txt
├── helLo.txt
├── yeah.txt
└── YEAH.txt
0 directories, 5 files
$ gawk 'BEGINFILE {if ((a[tolower(FILENAME)]++)) print FILENAME; nextfile}' *
helLo.txt
YEAH.txt
答案 10 :(得分:-2)
我只是在CentOS上使用fdupes来清理整个buncha重复文件......
yum install fdupes