
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.txt
no 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 -t
linha 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.