不区分大小写的重复文件名搜索


Answers:


14

如果您有可用的GNU实用程序(或至少有一个可以处理零终止行的集合),则另一个答案的方法很好:

find . -maxdepth 1 -print0 | sort -z | uniq -diz

注意:输出将具有零终止的字符串;您用于进一步处理的工具应该能够处理该问题。

在没有用于处理以零结尾的行的工具的情况下,或者如果您想确保代码在无法使用这些工具的环境中运行,则需要一个小的脚本:

#!/bin/sh
for f in *; do
  find . -maxdepth 1 -iname ./"$f" -exec echo \; | wc -l | while read count; do
    [ $count -gt 1 ] && echo $f
  done
done

这是什么疯狂?有关使疯狂文件名安全的技术的说明,请参见此答案


1
我本来打算发布类似的内容……但更糟糕的答案是:)
rozcietrzewiacz 2011年

2
您真的需要-mindepth吗?
rozcietrzewiacz 2011年

我正在使用Solaris。/ usr / bin / find是您正在谈论的那个吗?我尝试使用它,但给了我很多错误。
lamcro 2011年

@lamcro不,Solaris不使用GNU的find;我已经编辑了答案,以包括非GNU解决方案。
肖恩·高夫

好。我是否仅将其粘贴到文本文件中并赋予其执行权限?
lamcro 2011年

12

上面有很多复杂的答案,这似乎比所有答案都简单和快捷:

find . -maxdepth 1 | sort -f | uniq -di

如果要在子目录中找到重复的文件名,则只需比较文件名,而不要比较整个路径:

find . -maxdepth 2 -printf "%f\n" | sort -f | uniq -di

编辑:Shawn J. Goff指出,如果文件名带有换行符,则此操作将失败。如果您使用的是GNU实用程序,则也可以使它们起作用:

find . -maxdepth 1 -print0 | sort -fz | uniq -diz

-print0(用于发现)和-z选项(sort和uniq)导致他们对NULL结尾的字符串的工作,而不是换行终止字符串。由于文件名不能包含NUL,因此这适用于所有文件名。


1
但是,请参阅我对Shawn J. Goff答案的评论,您可以添加-print0选项来查找,以及-z选项来uniq和sort。另外,您还希望-f排序。然后就可以了。(我将对此进行编辑,如果您不同意,请随时回复)
derobert 2012年

最后一条命令给我的输出没有回车符(结果全部在一行中)。我正在使用Red Hat Linux运行命令。第一个命令行最适合我。
太阳

2

以不区分大小写的方式对文件名列表进行排序并打印重复项。sort有一个不区分大小写的排序选项。GNU也是如此uniq,但其他实现则没有,您所能做的uniq就是打印一组重复项中的每个元素,但遇到的第一个除外。使用GNU工具,假设没有文件名包含换行符,有一种简单的方法来打印所有元素,但每组重复项中只有一个:

for x in *; do printf "%s\n" "$x"; done |
sort -f |
uniq -id

可移植地,假设没有文件名包含换行符,则打印每组重复项中的所有元素:

for x in *; do printf "%s\n" "$x"; done |
sort -f |
awk '
    tolower($0) == tolower(prev) {
        print prev;
        while (tolower($0) == tolower(prev)) {print; getline}
    }
    1 { prev = $0 }'

如果需要容纳包含换行符的文件名,请使用Perl或Python。请注意,您可能需要调整输出,或者最好用相同的语言进行进一步处理,因为下面的示例代码使用换行符在其自己的输出中分隔名称。

perl -e '
    foreach (glob("*")) {push @{$f{lc($_)}}, $_}
    foreach (keys %f) {@names = @{$f{$_}}; if (@names > 1) {print "$_\n" foreach @names}}
'

这是一个纯zsh解决方案。这有点冗长,因为没有内置方法将重复元素保留在数组或全局结果中。

a=(*)(N); a=("${(@io)a}")
[[ $#a -le 1 ]] ||
for i in {2..$#a}; do
  if [[ ${(L)a[$i]} == ${(L)a[$((i-1))]} ]]; then
    [[ ${(L)a[$i-2]} == ${(L)a[$((i-1))]} ]] || print -r $a[$((i-1))]
    print -r $a[$i]
  fi
done

1

没有GNU find

LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $2}'


2
tr非常有可能肆虐上使用比每个字符一个字节以上的任何字符集。使用时,只有UTF-8的前256个字符是安全的tr。从维基百科TR(UNIX) .. 大多数版本的tr,包括GNU tr和经典的Unix tr,运行在单字节,并且不支持Unicode标准..
Peter.O

1
更新到我之前的评论。.只有UTF-8 的前128个字符是安全的。序数范围0..127以上的所有UTF-8字符 都是多字节的,并且在其他字符中可以具有单独的字节值。只有0..127范围的字节才与 唯一字符一对一关联。
Peter.O 2012年

加号uniq具有不区分大小写的标志i。
杰米·基特森

1

我终于以这种方式管理它:

find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d

我使用find而不是ls因为我需要包含完整路径(许多子目录)。我没有找到如何使用ls


2
双方sortuniq分别有忽略大小写的标志,f和我。
杰米·基特森

-1

对于任何其他想要重命名等文件之一的人:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.