Classifique os arquivos pelo número mais alto no nome do arquivo

Classifique os arquivos pelo número mais alto no nome do arquivo

Eu tenho um monte de arquivos todos nomeados assim:

name_file-1.txt
name_file-2.txt
name_file-3.txt
some_other_file-1.txt
some_other_file-2.txt

Existem milhares de nomes de arquivos diferentes, alguns com apenas um -1.txtno final, outros com -1.txt, -2.txt...-60.txt

Preciso copiar os números mais altos de cada arquivo, então name_file-3.txt, some_other_file-2.txt. Como faço isso em uma linha de comando do Linux?

Responder1

Com zsh:

typeset -A greatest
for f (*-*(n)) greatest[${f%-*}]=$f
cp -- $greatest /destination
  • *-*(n): arquivos não ocultos cujo nome contém um -( *-*), classificados numericamente ( (n)qualificador glob).
  • ${f%-*}: parte do nome do arquivo até a extremidade direita -(ou até o final, se não houver -).
  • $greatest: expande para o não vaziovaloresdas matrizes associativas. Então aqui, para arquivos que compartilham a mesma raiz, apenas o arquivo com maior número será expandido.

Responder2

files=(*)
mapfile -t prefixes < <(printf "%s\n" "${files[@]%-*}" | sort -u)
for p in "${prefixes[@]}"; do ls -v "$p"* | tail -1; done
name_file-3.txt
some_other_file-2.txt

E então copiá-los para algum outro diretório:

for ...; done | xargs cp -t /destination/directory

Responder3

Se os arquivos estiverem no diretório de trabalho atual e seus nomes estiverem em conformidade com as amostras (um único traço que precede um número), o seguinte pipeline compatível com POSIX deverá funcionar:

ls | sort -t- -k1,1 -k2,2rn | awk -F- 'k!=$1 {print; k=$1}' | pax -rw /path/to/dir

O componente awk pode ser substituído por um sort -u, se a opção -u do sort for estável (de modo que a primeira linha de um conjunto seja sempre escolhida para representar esse conjunto). O POSIX não exige essa estabilidade, mas, de acordo com seus manuais, as implementações {Free,Net,Open}BSD e GNU a fornecem. Se você gosta de desafiar o destino:

ls | sort -t- -k1,1 -k2,2rn | sort -mut- -k1,1 | pax -rw /path/to/dir

Em ambos os casos, o diretório de destino não deve estar no diretório de trabalho atual.

Responder4

Eu dividiria o arquivo em partes delimitadas por tabulações para uma análise de nome de arquivo mais confiável e personalizável e, em seguida, usaria o awk para encontrar a classificação mais alta de cada uma e relatar. Experimente cada parte do pipeline antes de prosseguir!

find DIR -type f <other find criteron> -print | 
perl -lne 'print join("\t",(/^(.*?-)(\d+)(\.\w+)$/))' |
awk -F\\t '$2 > f[$1] { f[$1]=$2;e[$1]=$3; } END { for (k in f) { print k f[k] e[k] }}' |
xargs cp -t <desination_directory>

O script awk coloca cada nome de arquivo em uma entrada de array associada, sempre mantendo a classificação mais alta encontrada. A extensão é armazenada em seu próprio array. Depois que todas as entradas forem processadas, todas as entradas da matriz serão geradas, uma por linha. A xargs cp -tlinha copia todos os arquivos para o diretório especificado.

Existe outro método quenão vai funcionarmuito bem se os números forem maiores que 9 e não forem preenchidos com 0. Esse método classifica os arquivos lexicograficamente e, ao analisar a lista, a primeira parte muda e o nome do arquivo visto mais recentemente é usado. Quando os nomes dos arquivos são assim, não funcionará:

file-9.txt
file-10.txt

porque o arquivo-10.txt aparecerá antes do arquivo-9. O script awk acima faz uma comparação numérica.

CAVEAT: Nomes de arquivos com tabulações e novas linhas causarão obstrução.

CAVEAT 2: Se forem possíveis múltiplas extensões por prefixo de nome de arquivo, teremos que fazer alguns ajustes para acertar.

informação relacionada