Como posso encontrar todas as extensões de arquivo distintas em uma hierarquia de pastas?

235

Em uma máquina Linux, eu gostaria de percorrer uma hierarquia de pastas e obter uma lista de todas as extensões de arquivo distintas nela.

Qual seria a melhor maneira de conseguir isso com um shell?

GloryFish
fonte

Respostas:

347

Tente isso (não tenho certeza se é a melhor maneira, mas funciona):

find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u

Funciona da seguinte forma:

  • Encontre todos os arquivos da pasta atual
  • Imprime a extensão dos arquivos, se houver
  • Faça uma lista classificada única
Ivan Nevostruev
fonte
8
apenas para referência: se você deseja excluir alguns diretórios da pesquisa (por exemplo .svn), use find . -type f -path '*/.svn*' -prune -o -print | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u source
Dennis Golomazov
Os espaços não farão diferença. Cada nome de arquivo estará em uma linha separada, portanto, o delimitador da lista de arquivos "\ n" não terá espaço.
Ivan Nevostruev 20/08/2013
1
No Windows, isso funciona melhor e é muito mais rápido que o localizador: dir / s / b | perl -ne 'imprime $ 1 se m /\.([^^.\\\\++)$/' | sort -u
Ryan Shillington
3
variação git da resposta: use em git ls-tree -r HEAD --name-onlyvez defind
jakub.g 4/15/15
8
Uma variação, isso mostra a lista com contagens por extensão:find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort | uniq -c | sort -n
marcovtwout
55

Não é necessário o pipe sort, o awk pode fazer tudo:

find . -type f | awk -F. '!a[$NF]++{print $NF}'
SiegeX
fonte
Não estou conseguindo que isso funcione como um alias, estou obtendo awk: erro de sintaxe no contexto da linha de origem 1 é >>>! A [] <<< awk: salvando na linha de origem 1. O que estou fazendo de errado? Meu alias é definido assim: alias file_ext = "find. -Type f -name ' . ' | Awk -F. '! A [$ NF] ++ {print $ NF}'"
user2602152
2
@ user2602152 o problema é que você está tentando envolver todo o verso com aspas para o aliascomando, mas o próprio comando já usa aspas no comando find. Para consertar isso, eu usaria basha sintaxe literal de strings da seguinte maneira:alias file_ext=$'find . -type f -name "*.*" | awk -F. \'!a[$NF]++{print $NF}\''
SiegeX 14/03/15
isso não funciona se um subdir possui um. em seu nome e o arquivo não tem extensão. Exemplo: quando fugimos do maindir, ele falharámaindir/test.dir/myfile
Nelson Teixeira
1
@NelsonTeixeira Adicione -printf "%f\n"ao final do comando 'find' e execute novamente o seu teste.
SiegeX 03/04
41

Versão recursiva:

find . -type f | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u

Se você deseja totais (quantas vezes a extensão foi vista):

find . -type f | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort | uniq -c | sort -rn

Não recursivo (pasta única):

for f in *.*; do printf "%s\n" "${f##*.}"; done | sort -u

Baseei isso nesta postagem do fórum , o crédito deve ir para lá.

ChristopheD
fonte
Ótimo! também funciona para o meu cenário git, estava tentando descobrir que tipo de arquivo toquei no último commit:git show --name-only --pretty="" | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u
vulcan raven
30

Powershell:

dir -recurse | select-object extension -unique

Obrigado a http://kevin-berridge.blogspot.com/2007/11/windows-powershell.html

Simon R
fonte
20
O OP disse "Em uma máquina Linux"
Forbesmyester
9
atualmente existe um pré-lançamento para linux: github.com/Microsoft/PowerShell-DSC-for-Linux
KIC
4
Como está escrito, isso também selecionará os diretórios que contêm um .(por exemplo jquery-1.3.4, aparecerão como .4na saída). Mude para dir -file -recurse | select-object extension -uniquepara obter apenas extensões de arquivo.
Mcw # 5/18
1
@Forbesmyester: Pessoas com Windows (como eu) encontrarão esta pergunta para. Então isso é útil.
Roel
1
Obrigado pela resposta do PowerShell. Você não assume como os usuários pesquisam. Muitas pessoas votaram por uma razão
Mahesh
20

Minha alternativa compatível com POSIX: awk-less, sed-less, Perl-less e Python-less:

find . -type f | rev | cut -d. -f1 | rev  | tr '[:upper:]' '[:lower:]' | sort | uniq --count | sort -rn

O truque é que ele inverte a linha e corta a extensão no início.
Também converte as extensões em minúsculas.

Exemplo de saída:

   3689 jpg
   1036 png
    610 mp4
     90 webm
     90 mkv
     57 mov
     12 avi
     10 txt
      3 zip
      2 ogv
      1 xcf
      1 trashinfo
      1 sh
      1 m4v
      1 jpeg
      1 ini
      1 gqv
      1 gcs
      1 dv
Ondra Žižka
fonte
no mac, uniqnão tem a bandeira completa --count, mas -cfunciona muito bem
worc 28/01
12

Encontre tudo com um ponto e mostre apenas o sufixo.

find . -type f -name "*.*" | awk -F. '{print $NF}' | sort -u

se você souber que todo sufixo tem 3 caracteres,

find . -type f -name "*.???" | awk -F. '{print $NF}' | sort -u

ou com sed mostra todos os sufixos com um a quatro caracteres. Altere {1,4} para o intervalo de caracteres que você espera no sufixo.

find . -type f | sed -n 's/.*\.\(.\{1,4\}\)$/\1/p'| sort -u
user224243
fonte
1
Não é necessário que o pipe 'ordene', o awk pode fazer tudo: encontre. -type f -name " . " | awk -F. '! a [$ NF] ++ {print $ NF}'
SiegeX
@SiegeX O seu deve ser uma resposta separada. Ele descobriu que o comando funciona melhor para pastas grandes, pois imprime as extensões conforme as encontra. Mas observe que deve ser: -name " . " #
Ralf
@ Ralf feito, postou resposta aqui . Não tem a certeza sobre o que você quer dizer com a -name "."coisa, porque isso é o que já está
SiegeX
Eu quis dizer que deveria ser -name "*. *", Mas o StackOverflow remove os caracteres *, o que provavelmente aconteceu no seu comentário também.
24411 Ralf
Parece que esta deve ser a resposta aceita, o awk é preferível ao perl como uma ferramenta de linha de comando e adota a filosofia unix de canalizar pequenos programas interoperáveis ​​em procedimentos coesos e legíveis.
Jon z
7

Adicionando minha própria variação à mistura. Eu acho que é o mais simples do lote e pode ser útil quando a eficiência não é uma grande preocupação.

find . -type f | grep -o -E '\.[^\.]+$' | sort -u
gkb0986
fonte
1
+1 para portabilidade, embora o regex seja bastante limitado, pois corresponde apenas a extensões que consistem em uma única letra. Usar o regex da resposta aceita parece melhor:$ find . -type f | grep -o -E '\.[^.\/]+$' | sort -u
mMontu
1
Acordado. Eu me afastei um pouco lá. Editando minha resposta para corrigir o erro que você viu.
precisa saber é o seguinte
legal. Eu chingo citações para aspas duplas, atualize biraries e dependências grep (porque o git está desatualizado) e agora esse trabalho é feito no windows. sinta-se como usuário do linux.
msangel
5

No Python, use geradores para diretórios muito grandes, incluindo extensões em branco, e obtenha o número de vezes que cada extensão aparece:

import json
import collections
import itertools
import os

root = '/home/andres'
files = itertools.chain.from_iterable((
    files for _,_,files in os.walk(root)
    ))
counter = collections.Counter(
    (os.path.splitext(file_)[1] for file_ in files)
)
print json.dumps(counter, indent=2)
Andres Restrepo
fonte
5

Eu tentei um monte de respostas aqui, mesmo a "melhor" resposta. Todos ficaram aquém do que eu estava procurando especificamente. Portanto, além das últimas 12 horas sentado no código regex para vários programas e lendo e testando essas respostas, é isso que eu criei que funciona EXATAMENTE como eu quero.

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • Localiza todos os arquivos que podem ter uma extensão.
  • Greps apenas a extensão
  • Greps para extensões de arquivo entre 2 e 16 caracteres (apenas ajuste os números se eles não atenderem à sua necessidade). Isso ajuda a evitar arquivos de cache e arquivos do sistema (o bit do arquivo do sistema é procurar prisão).
  • Awk para imprimir as extensões em minúsculas.
  • Classifique e traga apenas valores exclusivos. Originalmente, eu tentara tentar a resposta awk, mas ela duplicaria os itens que variavam na distinção entre maiúsculas e minúsculas.

Se você precisar de uma contagem das extensões de arquivo, use o código abaixo

find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort | uniq -c | sort -rn

Embora esses métodos levem algum tempo para serem concluídos e provavelmente não sejam as melhores maneiras de solucionar o problema, eles funcionam.

Atualização: as extensões de arquivo longas por @ alpha_989 causarão um problema. Isso se deve ao regex original "[[: alpha:]] {3,6}". Atualizei a resposta para incluir a regex "[[: alpha:]] {2,16}". No entanto, qualquer pessoa que use esse código deve estar ciente de que esses números são o mínimo e o máximo de quanto tempo a extensão é permitida para a saída final. Qualquer coisa fora desse intervalo será dividida em várias linhas na saída.

Nota: A postagem original leu "- Greps para extensões de arquivo entre 3 e 6 caracteres (basta ajustar os números se eles não atenderem às suas necessidades). Isso ajuda a evitar arquivos em cache e arquivos do sistema (o bit do arquivo do sistema é procurar prisão). "

Idéia: pode ser usada para encontrar extensões de arquivos com um comprimento específico via:

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{4,}" | awk '{print tolower($0)}' | sort -u

Onde 4 é o comprimento das extensões de arquivo a incluir e, em seguida, encontre também as extensões além desse comprimento.

Shinrai
fonte
A versão da contagem é recursiva?
Fernando Montoya
@Shinrai, Em geral, funciona bem. mas se você tiver algumas extensões de arquivo aleatórias muito longas, como .download, ele dividirá o ".download" em duas partes e informará dois arquivos, um que é "downlo" e outro que é "ad"
alpha_989
@ alpha_989, isso é devido ao regex "[[: alpha:]] {3,6}" também causará um problema com extensões menores que 3 caracteres. Ajuste para o que você precisa. Pessoalmente, eu diria que 2,16 deve funcionar na maioria dos casos.
Shinrai 04/04
Obrigado por responder .. Sim ... foi o que eu percebi mais tarde. Funcionou bem depois que eu o modifiquei, semelhante ao que você mencionou.
Alpha_989 4/04
3

Como já existe outra solução que usa Perl:

Se você possui o Python instalado, também pode fazer (a partir do shell):

python -c "import os;e=set();[[e.add(os.path.splitext(f)[-1]) for f in fn]for _,_,fn in os.walk('/home')];print '\n'.join(e)"
ChristopheD
fonte
2

Até agora, nenhuma das respostas lida com nomes de arquivos com novas linhas corretamente (exceto os de ChristopheD, que chegaram quando eu estava digitando isso). O seguinte não é uma linha de shell, mas funciona e é razoavelmente rápido.

import os, sys

def names(roots):
    for root in roots:
        for a, b, basenames in os.walk(root):
            for basename in basenames:
                yield basename

sufs = set(os.path.splitext(x)[1] for x in names(sys.argv[1:]))
for suf in sufs:
    if suf:
        print suf

fonte
2

Eu não acho que este foi mencionado ainda:

find . -type f -exec sh -c 'echo "${0##*.}"' {} \; | sort | uniq -c
Dmitry B.
fonte
Isso provavelmente seria bastante lento devido à geração de um novo processo para cada arquivo.
Ondra Žižka
1

Eu acho que a maneira mais simples e direta é

for f in *.*; do echo "${f##*.}"; done | sort -u

É modificado na 3ª via de ChristopheD.

Robert
fonte
0

você também pode fazer isso

find . -type f -name "*.php" -exec PATHTOAPP {} +
jrock2004
fonte
0

Achei simples e rápido ...

   # find . -type f -exec basename {} \; | awk -F"." '{print $NF}' > /tmp/outfile.txt
   # cat /tmp/outfile.txt | sort | uniq -c| sort -n > tmp/outfile_sorted.txt
Diego Callejo
fonte
0

A resposta aceita usa REGEX e você não pode criar um comando alternativo com REGEX, você deve colocá-lo em um script de shell, estou usando o Amazon Linux 2 e fiz o seguinte:

  1. Coloquei o código de resposta aceito em um arquivo usando:

    sudo vim find.sh

adicione este código:

find ./ -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u

salve o arquivo digitando: :wq!

  1. sudo vim ~/.bash_profile

  2. alias getext=". /path/to/your/find.sh"

  3. :wq!

  4. . ~/.bash_profile

Chris Medina
fonte