¿Cómo puedo encontrar todas las extensiones de archivo distintas en una jerarquía de carpetas?

235

En una máquina Linux me gustaría recorrer una jerarquía de carpetas y obtener una lista de todas las extensiones de archivo distintas dentro de ella.

¿Cuál sería la mejor manera de lograr esto desde un shell?

GloryFish
fuente

Respuestas:

347

Pruebe esto (no estoy seguro si es la mejor manera, pero funciona):

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

Funciona de la siguiente manera:

  • Encuentra todos los archivos de la carpeta actual
  • Imprime la extensión de los archivos, si los hay.
  • Haga una lista ordenada única
Ivan Nevostruev
fuente
8
solo como referencia: si desea excluir algunos directorios de la búsqueda (por ejemplo .svn), use la find . -type f -path '*/.svn*' -prune -o -print | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u fuente
Dennis Golomazov
Los espacios no harán ninguna diferencia. Cada nombre de archivo estará en una línea separada, por lo que el delimitador de la lista de archivos será "\ n", no espacio.
Ivan Nevostruev
1
En Windows, esto funciona mejor y es mucho más rápido que find: dir / s / b | perl -ne 'print $ 1 if m /\.([^^.\\\\font>+)$/' | sort -u
Ryan Shillington
3
git variación de la respuesta: use en git ls-tree -r HEAD --name-onlylugar defind
jakub.g
8
Una variación, esto muestra la lista con conteos por extensión:find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort | uniq -c | sort -n
marcovtwout
55

No es necesario que la tubería sortlo haga, awk puede hacerlo todo:

find . -type f | awk -F. '!a[$NF]++{print $NF}'
SiegeX
fuente
No estoy haciendo que esto funcione como un alias, estoy obteniendo awk: el error de sintaxis en el contexto de la línea de origen 1 es >>>! A [] <<< awk: rescate en la línea de origen 1. ¿Qué estoy haciendo mal? Mi alias se define así: alias file_ext = "find. -Type f -name ' . ' | Awk -F. '! A [$ NF] ++ {print $ NF}'"
user2602152
2
@ user2602152 el problema es que está tratando de rodear todo el one-liner con comillas para el aliascomando pero el comando en sí ya usa comillas en el comando find. Para solucionar este problema yo usaría bashEs sintaxis de cadena literal como tan:alias file_ext=$'find . -type f -name "*.*" | awk -F. \'!a[$NF]++{print $NF}\''
SiegeX
esto no funciona si un subdirectorio tiene un. en su nombre y el archivo no tiene extensión de archivo. Ejemplo: cuando huimos de maindir fallará paramaindir/test.dir/myfile
Nelson Teixeira
1
@NelsonTeixeira Agregue -printf "%f\n"al final del comando 'buscar' y vuelva a ejecutar su prueba.
SiegeX
41

Versión recursiva:

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

Si desea totales (cuántas veces se vio la extensión):

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

No recursivo (carpeta única):

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

He basado esto en esta publicación del foro , el crédito debería ir allí.

ChristopheD
fuente
¡Excelente! también funciona para mi escenario git, estaba tratando de averiguar qué tipo de archivos he tocado en la última confirmación:git show --name-only --pretty="" | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort -u
vulcan raven
30

Potencia Shell:

dir -recurse | select-object extension -unique

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

Simon R
fuente
20
El OP dijo "En una máquina Linux"
Forbesmyester
99
En realidad, ahora hay prowershell para Linux: github.com/Microsoft/PowerShell-DSC-for-Linux
KIC
44
Tal como está escrito, esto también recogerá los directorios que tienen un .(por ejemplo, jquery-1.3.4se mostrará como .4en la salida). Cambie a dir -file -recurse | select-object extension -uniquepara obtener solo extensiones de archivo.
mcw
1
@Forbesmyester: las personas con Windows (como yo) encontrarán esta pregunta. Entonces esto es útil.
Roel
1
Gracias por la respuesta Powershell. No asumes cómo buscan los usuarios. Mucha gente votó por una razón
Mahesh
20

Mi alternativa compatible con POSIX, awk-less, sed-less, Perl-less, Python-less:

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

El truco es que invierte la línea y corta la extensión al principio.
También convierte las extensiones a minúsculas.

Salida de ejemplo:

   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
fuente
en mac, uniqno tiene la bandera completa --count, pero -cfunciona bien
worc
12

Encuentra todo con un punto y muestra solo el sufijo.

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

si sabes que todos los sufijos tienen 3 caracteres, entonces

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

o con sed muestra todos los sufijos con uno a cuatro caracteres. Cambie {1,4} al rango de caracteres que espera en el sufijo.

find . -type f | sed -n 's/.*\.\(.\{1,4\}\)$/\1/p'| sort -u
usuario224243
fuente
1
No es necesario que la tubería se 'clasifique', awk puede hacerlo todo: buscar. -type f -name " . " | awk -F. '! a [$ NF] ++ {print $ NF}'
SiegeX
@SiegeX La suya debería ser una respuesta separada. Encontró que el comando funciona mejor para carpetas grandes, ya que imprime las extensiones a medida que las encuentra. Pero tenga en cuenta que debería ser: -name " . "
Ralf
@Ralf hecho, respuesta publicada aquí . No estoy muy seguro de lo que quieres decir con la -name "."cosa porque eso es lo que ya es
SiegeX
Quise decir que debería ser -name "*. *", Pero StackOverflow elimina los caracteres *, lo que probablemente también sucedió en tu comentario.
Ralf
Parece que esta debería ser la respuesta aceptada, awk es preferible a Perl como una herramienta de línea de comandos y abarca la filosofía de Unix de canalizar pequeños programas interoperables en procedimientos coherentes y legibles.
Jon z
7

Agregar mi propia variación a la mezcla. Creo que es el más simple de todos y puede ser útil cuando la eficiencia no es una gran preocupación.

find . -type f | grep -o -E '\.[^\.]+$' | sort -u
gkb0986
fuente
1
+1 para portabilidad, aunque la expresión regular es bastante limitada, ya que solo coincide con extensiones que consisten en una sola letra. Usar la expresión regular de la respuesta aceptada parece mejor:$ find . -type f | grep -o -E '\.[^.\/]+$' | sort -u
mMontu
1
Convenido. Me relajé un poco allí. Editando mi respuesta para corregir el error que viste.
gkb0986
frio. Cambio las comillas por comillas dobles, actualizo las bibliotecas grep y las dependencias (porque lo provisto con git está desactualizado) y ahora esto funciona en Windows. sentirse como un usuario de Linux.
msangel
5

En Python, use generadores para directorios muy grandes, incluidas extensiones en blanco, y obtenga la cantidad de veces que aparece cada extensión:

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
fuente
5

Intenté muchas de las respuestas aquí, incluso la "mejor" respuesta. Todos se quedaron cortos de lo que yo buscaba específicamente. Entonces, además de las últimas 12 horas de estar en código regex para múltiples programas y leer y probar estas respuestas, esto es lo que se me ocurrió y funciona EXACTAMENTE como quiero.

 find . -type f -name "*.*" | grep -o -E "\.[^\.]+$" | grep -o -E "[[:alpha:]]{2,16}" | awk '{print tolower($0)}' | sort -u
  • Encuentra todos los archivos que pueden tener una extensión.
  • Greps solo la extensión
  • Greps para extensiones de archivo de entre 2 y 16 caracteres (solo ajuste los números si no se ajustan a sus necesidades). Esto ayuda a evitar los archivos de caché y los archivos del sistema (el bit del archivo del sistema es buscar en la cárcel).
  • Awk para imprimir las extensiones en minúsculas.
  • Ordenar y aportar solo valores únicos. Originalmente había intentado probar la respuesta awk, pero duplicaría los elementos que varían en mayúsculas y minúsculas.

Si necesita un recuento de las extensiones de archivo, use el siguiente código

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

Si bien estos métodos tardarán un tiempo en completarse y probablemente no sean las mejores formas de solucionar el problema, funcionan.

Actualización: las extensiones de archivo largas @ alpha_989 causarán un problema. Eso se debe a la expresión regular original "[[: alpha:]] {3,6}". He actualizado la respuesta para incluir la expresión regular "[[: alpha:]] {2,16}". Sin embargo, cualquiera que use este código debe tener en cuenta que esos números son el mínimo y el máximo de cuánto tiempo se permite la extensión para la salida final. Cualquier cosa fuera de ese rango se dividirá en varias líneas en la salida.

Nota: La publicación original sí decía "- Greps para extensiones de archivo de entre 3 y 6 caracteres (solo ajuste los números si no se ajustan a sus necesidades). Esto ayuda a evitar archivos de caché y archivos del sistema (el bit del archivo del sistema es buscar en la cárcel). "

Idea: podría usarse para buscar extensiones de archivo de una longitud específica mediante:

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

Donde 4 es la longitud de las extensiones de archivo a incluir y luego también encuentra cualquier extensión más allá de esa longitud.

Shinrai
fuente
¿La versión de recuento es recursiva?
Fernando Montoya
@Shinrai, en general funciona bien. pero si tiene algunas extensiones de archivo aleatorias que son realmente largas, como .download, dividirá el ".download" en 2 partes e informará 2 archivos, uno que es "downlo" y otro que es "ad"
alpha_989
@ alpha_989, Eso se debe a la expresión regular "[[: alpha:]] {3,6}" también causará un problema con extensiones de menos de 3 caracteres. Ajústese a lo que necesita. Personalmente, diría que 2,16 debería funcionar en la mayoría de los casos.
Shinrai
Gracias por responder ... Sí ... eso es lo que me di cuenta más tarde. Funcionó bien después de que lo modifiqué similar a lo que mencionaste.
alpha_989
3

Como ya hay otra solución que usa Perl:

Si tiene instalado Python, también podría hacer (desde el 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
fuente
2

Ninguna de las respuestas hasta el momento trata con nombres de archivo con líneas nuevas correctamente (excepto ChristopheD, que apareció cuando estaba escribiendo esto). Lo siguiente no es una línea única de shell, pero funciona, y es razonablemente 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

fuente
2

No creo que este haya sido mencionado todavía:

find . -type f -exec sh -c 'echo "${0##*.}"' {} \; | sort | uniq -c
Dmitry B.
fuente
Esto probablemente sería bastante lento debido a la generación de un nuevo proceso para cada archivo.
Ondra Žižka
1

Creo que la forma más simple y directa es

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

Se modificó en la tercera vía de ChristopheD.

Robert
fuente
0

también podrías hacer esto

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

Lo he encontrado simple y 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
fuente
0

La respuesta aceptada usa REGEX y no puede crear un comando de alias con REGEX, debe ponerlo en un script de shell, estoy usando Amazon Linux 2 e hice lo siguiente:

  1. Puse el código de respuesta aceptado en un archivo usando:

    sudo vim find.sh

agregue este código:

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

guarde el archivo escribiendo: :wq!

  1. sudo vim ~/.bash_profile

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

  3. :wq!

  4. . ~/.bash_profile

Chris Medina
fuente