给定一个包含各种文件的嵌套目录结构,我想找到其中的所有文件,但是如果有多个同名文件,我想只返回最大的文件。
因此,例如,给定一个如下的目录结构:
|--- foo.jpg (110 KB)
|--- bar.jpg (210 KB)
|--- dir
|----- foo.jpg (860 KB)
|----- baz.jpg (200 KB)
我想生成输出行(顺序不重要):
bar.jpg
dir/foo.jpg
dir/baz.jpg
我怎样才能做到这一点,最好是从bash?
答案1
充实@UlrichSchwarz 的评论,我最终得到:
find . -type f -printf "%s %P %f\n" | sort -k3,3 -k1,1rn | uniq -f 2 | cut -f 2 -d ' '
编辑这不会处理带有(例如)空格的文件名。请参阅@StéphaneChazelas 的解决方案以获得更强大的解决方案。
答案2
和zsh
:
typeset -A files
for f (**/*(D.oL)) files[$f:t]=$f
printf '%s\n' $files
可以使用文件名可能包含的任何字节或字符(如空格、换行符...)。
使用 GNU 工具:
find . -type f -printf '%s/%f/%P\0' |
sort -zrn |
LC_ALL=C sort -zt/ -uk2,2 |
tr '\0\n' '\n\0' |
cut -d/ -f3- |
tr '\0' '\n'
如果要删除重复项,请使用zsh
:
allfiles=(**/*(D.oL))
typeset -A best
for f ($allfiles) best[$f:t]=$f
bestfiles=($best)
dups=(${allfiles:|bestfiles})
rm -rf -- $dups
zsh的一些功能描述:
typeset -A best
:像 ksh93 中一样声明一个关联数组变量。最新版本bash
也支持它。**/*
: 递归通配符。由 zsh 在九十年代初引入,现在在其他一些 shell 中也发现了它的变化。(D.oL)
: 通配限定符。另一项 zsh 发明,尚未被其他 shell 复制,尽管它是递归通配符的重要伴侣。用于进一步限定 glob。D
包含点文件,.
仅包含常规的文件,oL
按长度排序(大小以字节为单位)。${file:t}
:就像 (t)csh 中一样,扩展到尾巴文件名的一部分(基本名称)。${a:|b}
a
扩展到中不存在的元素b
。 (a-b)。