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

4

Eu tenho vários arquivos 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.txt no final, alguns com -1.txt , -2.txt ... -60.txt

Eu 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?

    
por MIB 12.02.2016 / 11:59

6 respostas

4

com zsh :

typeset -A greatest
for f (*-*(n)) greatest[${f%-*}]=$f
cp -- $greatest /destination
  • *-*(n) : arquivos não ocultos cujo nome contém - ( *-* ), classificados numericamente ( (n) qualifier glob).
  • ${f%-*} : parte do nome do arquivo até a parte mais à direita - (ou até o final, se não houver - ).
  • $greatest : expande para os valores não vazios dos arrays associativos. Então, aqui, para arquivos que compartilham a mesma raiz, apenas o arquivo com o maior número será expandido.
por 12.02.2016 / 12:58
1
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 depois copiá-los para outro diretório:

for ...; done | xargs cp -t /destination/directory
    
por 12.02.2016 / 12:25
1

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 uma ordenação -u, se a opção -u da classificação for estável (para que a primeira linha de um conjunto seja sempre escolhida para representar esse conjunto). O POSIX não requer esta estabilidade, mas, de acordo com seus manuais, as implementações BSD e GNU {Free, Net, Open} o fornecem. Se você gosta de tentar 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.

    
por 13.02.2016 / 04:34
1

Se pwd é o diretório de origem

cp -t /path_to_destination $(
      ls -v *[0-9].txt |
      sed '$b;N;/^\(.*\)[0-9]\+\.txt\n[0-9]\+\.txt/!P;D')

NOTA: Se houver algum espaço nos nomes dos arquivos, ele deve ser preparado antes, fugindo para a operação correta. + veja outras restrições em Stéphane Chazelas 'comentários

    
por 12.02.2016 / 12:27
0

Eu dividi o arquivo em partes delimitadas por tabulação para uma análise de nome de arquivo mais confiável e personalizável, depois usei o awk para encontrar a classificação mais alta de cada e relatório. Experimente cada parte do pipeline antes de prosseguir para o próximo!

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 matriz associada, sempre mantendo a classificação mais alta encontrada. A extensão é armazenada em sua própria matriz. Depois que toda entrada é processada, todas as entradas da matriz são produzidas, uma por linha. A linha xargs cp -t copia todos os arquivos para o diretório especificado.

Existe outro método que não funcionará muito bem se os números forem maiores que 9 e não forem 0-preenchidos. Esse método classifica os arquivos de forma lexicográfica e, ao analisar a lista, a primeira parte é alterada, o nome do arquivo mais visto é usado. Quando os nomes dos arquivos são assim, não funciona:

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 abas e novas linhas causam um estrangulamento.

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

    
por 12.02.2016 / 12:31
0

Esta não é uma resposta de linha de comando, por si só, mas supondo que você tenha bash version > = 4 disponível, aqui está um script bash que reúne todos os arquivos * .txt, determina seus números suffix, em seguida, salva o sufixo de numeração mais alta visto em uma matriz associativa (indexada pela parte base do nome do arquivo antes do sufixo numerado). Ele imprime uma amostra de cp de comandos para cada nome de arquivo com o maior sufixo visto.

Ajuste o destino "somedir" conforme necessário.

#!/bin/bash

declare -A highest
for f in *.txt
do
  base=${f%%.txt}
  prefix=${base%-*}
  postfix=${base/$prefix-/}
  if [[ ${highest[$prefix]} -lt $postfix ]]
  then
    highest[$prefix]=$postfix
  fi
done

for prefix in "${!highest[@]}"
do
  escaped=${prefix//\"/\\"}
  printf "cp -- \"%s\" somedir/\n" "$escaped-${highest[$prefix]}.txt"
done

Em um diretório desses arquivos:

-dash-1.txt
-dash-2.txt
double"quote-3.txt
file'here-1.txt
filetwo'here'-2.txt
name_file-1.txt
name_file-2.txt
name_file-3.txt
somefile-5.txt
somefile-60.txt
some_other_file-1.txt
some_other_file-2.txt
space file-1.txt
space file-2.txt

A saída é (manualmente ordenada, para facilitar a leitura):

cp -- "-dash-2.txt" somedir/
cp -- "double\"quote-3.txt" somedir/
cp -- "file'here-1.txt" somedir/
cp -- "filetwo'here'-2.txt" somedir/
cp -- "name_file-3.txt" somedir/
cp -- "somefile-60.txt" somedir/
cp -- "some_other_file-2.txt" somedir/
cp -- "space file-2.txt" somedir/
    
por 12.02.2016 / 18:26