Encuentra el número de archivos para cada extensión en un directorio

10

Quiero contar el número de archivos para cada extensión en un directorio, así como los archivos sin extensión.

He probado algunas opciones, pero todavía no he encontrado una solución que funcione:

  • find "$folder" -type f | sed 's/.*\.//' | sort | uniq -ces una opción pero no funciona si no hay extensión de archivo. Necesito saber cuántos archivos no tienen una extensión.

  • También probé un bucle de búsqueda en una matriz y luego sumé los resultados, pero en este momento ese código arroja un error variable no declarado, pero solo fuera del bucle:

    declare -a arr
    arr=()
    echo ${arr[@]}
    

    Esto arroja una variable no declarada, así como una vez que se completa el ciclo de búsqueda.

niño tractor
fuente

Respuestas:

10
find "$path" -type f | sed -e '/.*\/[^\/]*\.[^\/]*$/!s/.*/(none)/' -e 's/.*\.//' | LC_COLLATE=C sort | uniq -c

Explicación:

  • find "$path" -type f obtener una lista recursiva de todos los archivos en la "$path"carpeta.
  • sed -e '/.*\/[^\/]*\.[^\/]*$/!s/.*/(none)/' -e 's/.*\.//' expresiones regulares:
    • /.*\/[^\/]*\.[^\/]*$/!s/.*/(none)/ reemplace todos los archivos sin extensión por (ninguno).
    • s/.*\.// obtener la extensión de los archivos restantes.
  • LC_COLLATE=C sort ordenar el resultado, manteniendo los símbolos en la parte superior.
  • uniq -c cuente el número de entradas repetidas.
Helio
fuente
9

Usando Python:

import os
from collections import Counter
from pprint import pprint

lst = []
for file in os.listdir('./'):
        name, ext = os.path.splitext(file)
        lst.append(ext)

pprint(Counter(lst))

La salida:

Counter({'': 7,
         '.png': 4,
         '.mp3': 3,
         '.jpg': 3,
         '.mkv': 3,
         '.py': 1,
         '.swp': 1,
         '.sh': 1})
Ravexina
fuente
Usted probablemente puede salirse con la lista por comprensión, como ext = [ f.split('.')[-1] for f in os.listdir('./') ] Thatll hacerla par de líneas más cortas y quizás más Pythonic
Sergiy Kolodyazhnyy
Gracias por su sugerencia, solo estaba tratando de escribirlo lo más claro posible ...
Ravexina
1
La claridad es la virtud :) Especialmente cuando se trata de código y documentación de ingeniería.
Sergiy Kolodyazhnyy
6

Si tienes GNU awk, podrías hacer algo como

printf '%s\0' * | gawk 'BEGIN{RS="\0"; FS="."; OFS="\t"} 
  {a[(NF>1 ? $NF : "(none)")]++} 
  END{for(i in a) print a[i],i}
'

es decir, construir / incrementar una matriz asociativa tecleada en el último .campo separado, o alguna cadena fija arbitraria, como (none)si no hubiera extensión.

mawkno parece permitir un separador de registro de byte nulo; podría usarlo mawkcon el separador de línea nueva predeterminado si está seguro de que no necesita tratar con líneas nuevas en los nombres de sus archivos:

printf '%s\n' * | mawk 'BEGIN{FS="."; OFS="\t"} {a[(NF>1 ? $NF : "(none)")]++} END{for(i in a) print a[i],i}'
conductor de acero
fuente
5

Con la tarea básica /bin/sho incluso bashpuede ser un poco difícil, pero como puede ver en otras respuestas, las herramientas que pueden trabajar en datos agregados pueden manejar esa tarea particularmente fácil. Una de esas herramientas sería la sqlitebase de datos.

El proceso muy simple para usar la sqlitebase de datos sería crear un .csvarchivo con dos campos: nombre de archivo y extensión. Más tarde sqlitese puede utilizar la instrucción simple agregado COUNT()de GROUP BY extllevar a cabo el recuento de archivos basado en campo de extensión

$ { printf "file,ext\n"; find -type f -exec sh -c 'f=${1##*/};printf "%s,%s\n" "${1}" "${1##*.}"' sh {} \; ; }  > files.csv
$ sqlite3 <<EOF
> .mode csv
> .import ./files.csv files_tb
> SELECT ext,COUNT(file) FROM files_tb GROUP BY ext;
> EOF
csv,1
mp3,6
txt,1
wav,27
Sergiy Kolodyazhnyy
fuente
files_tb¿Creo que se hace referencia a la tabla pero las columnas de la tabla no están definidas en ningún lugar que pueda ver?
WinEunuuchs2Unix
@ WinEunuuchs2Unix Se definen en el propio archivo csv. Eso es lo que hace el primero printf. Y SQLite usará de forma predeterminada el tratamiento de la primera línea del archivo csv como nombres de columna.
Sergiy Kolodyazhnyy
1
¡Muy impresionante! +1
WinEunuuchs2Unix
5

Usando PowerShell si esa es una opción:

Get-ChildItem -File | Group-Object Extension -NoElement

o más corto, usando alias:

ls -file | group -n Extension
Joey
fuente
1
¡Guauu! Gran primera respuesta! Ni siquiera sabía que PowerShell existía para Linux ... +1
Fabby
2
Gracias. Ha existido multiplataforma y de código abierto durante un tiempo, pero ha habido un patrón en SO y SU donde las preguntas para la creación de scripts de shell en Windows a menudo se han respondido con "Bueno, instale cygwin y use bash, luego puede hacer lo siguiente ", por lo que he dudado en hacer lo mismo para los sitios de Linux SE con herramientas que se originaron en Windows. Pero esta ha sido una buena tarea que muestra las fortalezas de PowerShell bastante bien sin invitar al viejo argumento sobre la verbosidad.
Joey