Como encontrar arquivos em subdiretórios e organizá-los por nome de arquivo em um único comando?

7

Resultado de um achado normal usando find . ! -path "./build*" -name "*.txt" :

./tool/001-sub.txt
./tool/000-main.txt
./zo/001-int.txt
./zo/id/002-and.txt
./as/002-mod.txt

e quando classificado com sort -n :

./as/002-mod.txt
./tool/000-main.txt
./tool/001-sub.txt
./zo/001-int.txt
./zo/id/002-and.txt

no entanto, a saída desejada é:

./tool/000-main.txt
./zo/001-int.txt
./tool/001-sub.txt
./zo/id/002-and.txt
./as/002-mod.txt

que significa que a saída é classificada com base apenas no nome do arquivo , mas as informações da pasta devem ser mantidas como parte da saída.

Editar : Torne o exemplo mais complicado, pois a estrutura do subdiretório pode incluir mais de um nível.

    
por unode 23.05.2011 / 03:15

3 respostas

9

Você precisa classificar pelo último campo (considerando / como um separador de campo). Infelizmente, não consigo pensar em uma ferramenta que possa fazer isso quando o número de campos varia (se apenas sort -k puder receber valores negativos).

Para contornar isso, você terá que fazer uma decorar-classificar-undecorate. Ou seja, pegue o nome do arquivo e coloque-o no início, seguido por um separador de campo, faça uma classificação e remova a primeira coluna e o separador de campo.

find . ! -path "./build*" -name "*.txt" |\
    awk -vFS=/ -vOFS=/ '{ print $NF,$0 }' |\
    sort -n -t / |\
    cut -f2- -d/

Esse comando awk diz que o separador de campos FS está definido como / ; isso afeta a maneira como lê os campos. O separador de campos de saída OFS também é definido como / ; isso afeta a maneira como imprime registros. A próxima instrução diz imprimir a última coluna ( NF é o número de campos no registro, portanto, também é o índice do último campo), bem como todo o registro ( $0 é o registro inteiro); ele irá imprimi-los com o OFS entre eles. Então a lista é sort ed, tratando / como o separador de campos - já que temos o nome do arquivo primeiro no registro, ele irá classificar por isso. Em seguida, o cut imprime somente os campos 2 até o final, tratando novamente / como o separador de campo.

    
por 23.05.2011 / 04:04
3

Em zsh ≥4.3.10:

print -l -- **/*.txt~build*(oe\''REPLY=${REPLY:t}'\')
  • **/*.txt corresponde *.txt no diretório atual e em seus subdiretórios recursivamente .
  • ~build* exclui correspondências cujo texto começa com build* (como ! -path './build*' ) . (Você precisa de setopt extended_glob primeiro.)
  • (oe\''…'\') é um qualificador da glob de classificação. REPLY=… constrói a string para classificar a string a ser retornada.
  • ${REPLY:t} é o basename (“tail”) do caminho.
por 23.05.2011 / 09:29
2

Eu usaria arquivos '-printf' para dar saída ao nome e caminho, classificar por nome e cortar o nome em uma última etapa. '###' é apenas um marcador, para ajudar no corte.

find -name "*.txt" -printf "%f###%p\n" | sort -n | sed 's/.*###//'

% f imprime o nome do arquivo,% p o caminho inteiro.

Eu simplifiquei o comando find para colocá-lo em uma linha, é claro que você deixaria a parte ! -path "./build*" .

    
por 23.05.2011 / 17:15