¿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?
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 echo
cuando esté contento con lo que se eliminará.
El punto .
representa el directorio actual. find
encuentra archivos y directorios de forma recursiva .
y puede hacer cosas con ellos.
-type
es una de find
las 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 -exec
primario (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 find
comando. Entonces, la primera -exec
llamada 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 -v
indicador 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 x
a 10
.
NR
es 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 -exec
llamada (si elimina echo
) eliminará cada archivo (que llega tan lejos en la evaluación de find
las primarias) ejecutando:
rm -f ./somefilename
Suponiendo una find
implementación que admita el -readable
predicado (si find
no 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 echo
si 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 x
XX 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 echo
cuando 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 print
con unlink
si es feliz.
sh
? 2. ¿Eswc -l < "$file"
más rápido quewc -l "$file"
? 3. ¿Cómo sabe sh el valor de$x
, que se define en el shell Bash que realiza la llamada?sh
es 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áwc
incluso si el archivo no se puede abrir.$x
se exporta afind
(x=10 find...
), que a su vez se lo pasash
.find: -readable: unknown primary or operator
.bash
.bash
es solo un intérprete de línea de comandos, pero de lafind
implementación.-readable
es una extensión de GNU, no está disponible en OS / Xfind
. 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 archivoswc
para los archivos que no son legibles.En aras de la exhaustividad, aparte de AWK también puede usar GNU sed para lograr el mismo resultado:
Lo que resulta en una línea de comando un poco más concisa.
Explicación
fuente