Forma rápida de eliminar archivos con menos de x líneas

10

¿Cuál es una forma rápida y no demasiado complicada de eliminar todos los archivos en un directorio que están debajo de x líneas de largo, en bash?

durrrutti
fuente

Respuestas:

10

Aquí hay una solución POSIX que debería ser bastante sencilla de entender:

find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;

Como en la respuesta de Stephane , elimine echocuando esté contento con lo que se eliminará.


Explicaciones, escritas para aquellos totalmente nuevos en Unix / Linux:

El punto .representa el directorio actual. findencuentra archivos y directorios de forma recursiva .y puede hacer cosas con ellos.

-typees una de findlas primarias ; es una prueba que se realizará para cada archivo y directorio que se encuentre de forma recursiva (dentro .), y el resto de las primarias en la línea solo se evalúan si esto resulta "verdadero".

En este caso particular, solo continuamos si tratamos con un archivo normal , no con un directorio u otra cosa (por ejemplo, un dispositivo de bloque).


El -execprimario (de find) llama a un comando externo y solo pasa al siguiente primario si el comando externo se cierra con éxito (estado de salida de "0"). El {}se reemplaza con el nombre de archivo "considerado" por el findcomando. Entonces, la primera -execllamada es equivalente al siguiente comando de shell, ejecutado para cada archivo a su vez:

awk -v x=10 'NR==x{exit 1}' ./somefilename

Awk es un lenguaje completo en sí mismo, diseñado para manejar archivos de texto delimitados como CSV. Los condicionales y comandos Awk (que se encuentran entre comillas simples y comienzan con las letras NR) se ejecutan para cada línea de un archivo de texto. (Bucle implícito).

Para aprender Awk por completo, recomiendo encarecidamente el Tutorial de Grymoire , pero explicaré las características de Awk utilizadas en el comando anterior.


El -vindicador a Awk nos permite establecer una variable Awk (una vez) antes de que se ejecuten los comandos Awk (para cada línea del archivo). En este caso, establecemos xa 10.


NRes una variable especial Awk referencia a la " N umber de la corriente R ECORD." En otras palabras, es el número de línea que estamos viendo en cualquier paso particular a través del ciclo.

(Nota que es posible, aunque raro, usar una diferente " R ECORD S eparator" que el valor predeterminado de un carácter de nueva línea, por el ajuste RS. Este es un ejemplo de jugar con separadores de discos. )


Las secuencias de comandos Awk en general consisten en condiciones (fuera de las llaves) combinadas con acciones (dentro de las llaves). Puede haber condiciones compuestas y acciones compuestas, y hay una condición predeterminada (verdadera) y una acción predeterminada (imprimir), pero no necesitamos No te molestes con esos.

La condición aquí es: "¿Es esta la décima línea?" Si este es el caso, salimos con un estado de salida distinto de cero, que en la secuencia de comandos de shell significa "terminación de comando fallida".

Por lo tanto, la única forma en que este comando Awk saldrá con éxito es si se alcanza el final del archivo antes de llegar a la línea 10.

Entonces, si el script Awk se cierra con éxito, significa que tiene un archivo de menos de diez líneas.


La siguiente -execllamada (si elimina echo) eliminará cada archivo (que llega tan lejos en la evaluación de findlas primarias) ejecutando:

rm -f ./somefilename
Comodín
fuente
5

Suponiendo una findimplementación que admita el -readablepredicado (si findno lo admite, simplemente elimínelo, solo recibirá mensajes de error para archivos no legibles o reemplácelos por -exec test -r {} \;):

x=10 find . -type f -readable -exec sh -c '
  for file do
    lines=$(wc -l < "$file") && [ "$((lines))" -lt "$x" ] && echo rm -f "$file"
  done' sh {} +

Eliminar el echosi feliz.

Eso no es particularmente eficiente en cuenta que todas las líneas en todos los archivos, mientras que sólo necesita parar en la xXX uno y se ejecuta uno wc(y potencialmente uno rm) comando para cada archivo.

Con GNU awk, puede hacerlo mucho más eficiente con:

x=10
find . -type f -readable -exec awk -v x="$x" -v ORS='\0' '
  FNR == x {nextfile}
  ENDFILE {if (FNR < x) print FILENAME}' {} +|
  xargs -r0 echo rm -f

(de nuevo, eliminar echocuando esté feliz).

Lo mismo con perl:

x=10 find . -type f -readable -exec perl -Tlne '
  if ($. == $ENV{x}) {close ARGV}
  elsif (eof) {print $ARGV; close ARGV}' {} +

Reemplazar printcon unlinksi es feliz.

Stéphane Chazelas
fuente
1. ¿Para qué es lo último sh? 2. ¿Es wc -l < "$file"más rápido que wc -l "$file"? 3. ¿Cómo sabe sh el valor de $x, que se define en el shell Bash que realiza la llamada?
3
@tomas, lo último shes lo que va en ese script en línea $0, para ser usado para mensajes de error, por ejemplo. wc -l "$file"imprimiría el nombre del archivo que no queremos aquí y se ejecutará wcincluso si el archivo no se puede abrir. $xse exporta a find( x=10 find...), que a su vez se lo pasa sh.
Stéphane Chazelas
¡Gracias! Pero supongo que este error que obtengo en OSX significa que mi versión Bash no es compatible con el indicador legible. find: -readable: unknown primary or operator.
durrrutti
1
@durrrutti, eso no se debe a bash. bashes solo un intérprete de línea de comandos, pero de la findimplementación. -readablees una extensión de GNU, no está disponible en OS / X find. Solo se usa para limitar los archivos que son legibles (no podrá obtener el recuento de líneas para archivos no legibles). Puede omitirlo para el primero, luego recibirá mensajes de error al abrir los archivos wcpara los archivos que no son legibles.
Stéphane Chazelas
@ StéphaneChazelas, esta respuesta es tan complicada que me pregunto: ¿Me perdí algunos casos extremos con mi respuesta? :)
Comodín el
2

En aras de la exhaustividad, aparte de AWK también puede usar GNU sed para lograr el mismo resultado:

find . -type f -exec sed 11q1 '{}' ';' -exec echo rm -f '{}' ';'

Lo que resulta en una línea de comando un poco más concisa.

Explicación

11 - is the address, i.e. "the eleventh line"
q - is for _q_uit (abort the execution)
1 - is the exit code parameter for q (GNU sed extension) 
zepelín
fuente