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

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

我有办法找到目录中具有重复文件名的所有文件,无论大小写(大写和/或小写)如何?

答案1

如果您有可用的 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

这是什么疯狂?看这个答案有关使疯狂文件名安全的技术的解释。

答案2

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

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(用于排序和 uniq)使它们适用于 NUL 终止的字符串,而不是换行符终止的字符串。由于文件名不能包含 NUL,因此这适用于所有文件名。

答案3

以不区分大小写的方式对文件名列表进行排序并打印重复项。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

答案4

没有 GNU find

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

相关内容