Encontre arquivos recursivamente, mas escolha o maior entre aqueles com nomes duplicados

3

Dada uma estrutura de diretórios aninhada contendo vários arquivos, eu gostaria de encontrar todos os arquivos dentro dela, mas onde houver vários arquivos com o mesmo nome, eu gostaria de retornar apenas o maior arquivo.

Assim, por exemplo, dada uma estrutura de diretórios como:

|--- foo.jpg (110 KB)
|--- bar.jpg (210 KB)
|--- dir
      |----- foo.jpg (860 KB)
      |----- baz.jpg (200 KB)

Gostaria de produzir as linhas de saída (ordem sem importância):

bar.jpg
dir/foo.jpg
dir/baz.jpg

Como posso fazer isso, de preferência do bash?

    
por Matt R 07.12.2015 / 09:28

2 respostas

1

com zsh :

typeset -A files
for f (**/*(D.oL)) files[$f:t]=$f
printf '%s\n' $files

Funcionaria com quaisquer bytes ou caracteres (como espaço, nova linha ...) que os nomes de arquivo possam conter.

Com ferramentas GNU:

find . -type f -printf '%s/%f/%P
allfiles=(**/*(D.oL))
typeset -A best
for f ($allfiles) best[$f:t]=$f
bestfiles=($best)
dups=(${allfiles:|bestfiles})
rm -rf -- $dups
' | sort -zrn | LC_ALL=C sort -zt/ -uk2,2 | tr '
typeset -A files
for f (**/*(D.oL)) files[$f:t]=$f
printf '%s\n' $files
\n' '\n
find . -type f -printf '%s/%f/%P
allfiles=(**/*(D.oL))
typeset -A best
for f ($allfiles) best[$f:t]=$f
bestfiles=($best)
dups=(${allfiles:|bestfiles})
rm -rf -- $dups
' | sort -zrn | LC_ALL=C sort -zt/ -uk2,2 | tr '%pre%\n' '\n%pre%' | cut -d/ -f3- | tr '%pre%' '\n'
' | cut -d/ -f3- | tr '%pre%' '\n'

Se você quiser remover as duplicatas, com zsh :

%pre%

Descrição de alguns recursos do zsh:

  • typeset -A best : declara uma variável de matriz associativa como em ksh93. Versões recentes de bash também o suportam.
  • **/* : globalização recursiva. Introduzido por zsh no início dos anos noventa, agora encontrado em algumas outras conchas com variações.
  • (D.oL) : qualificadores de globbing. Outra invenção, ainda não copiada por outras conchas, é uma companheira essencial para a globalização recursiva. Usado para qualificar ainda mais o glob. D para incluir arquivos de pontos, . para incluir apenas arquivos regulares , oL para ordenar por tamanho (tamanho em bytes).
  • ${file:t} : como em (t) csh, expande para a parte tail de um nome de arquivo (o nome da base).
  • ${a:|b} expande para os elementos de a que não estão em b . ( a - b ).
por 09.12.2015 / 16:02
2

Resumindo o comentário do @ UlrichSchwarz, acabei com:

find . -type f -printf "%s %P %f\n" | sort -k3,3 -k1,1rn | uniq -f 2 | cut -f 2 -d ' '

Editar isto não irá lidar com nomes de arquivos com espaços (por exemplo). Veja a solução @ StéphaneChazelas para algo mais robusto.

    
por 07.12.2015 / 10:27