Qu'est-ce qu'un moyen rapide et pas trop compliqué de supprimer tous les fichiers d'un répertoire de moins de x lignes, en bash?
Voici une solution POSIX qui devrait être assez simple à comprendre:
find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;
Comme dans la réponse de Stéphane , supprimez le echo
lorsque vous êtes satisfait de ce qui sera supprimé.
Le point .
représente le répertoire courant. find
trouve les fichiers et les répertoires de manière récursive à l'intérieur .
et peut faire des choses avec eux.
-type
est l' un des find
de » les primaires ; c'est un test qui sera effectué pour chaque fichier et répertoire qui se trouve récursivement (à l'intérieur .
), et les autres primaires sur la ligne ne sont évaluées que si cela aboutit à "true".
Dans ce cas particulier, nous ne continuons que si nous avons affaire à un fichier normal , pas à un répertoire ou à quelque chose d'autre (par exemple, un périphérique bloc).
Le -exec
primaire (de find
) appelle une commande externe et ne passe au primaire suivant que si la commande externe se termine avec succès (état de sortie "0"). Le {}
est remplacé par le nom de fichier "considéré" par la find
commande. Le premier -exec
appel équivaut donc à la commande shell suivante, exécutée tour à tour pour chaque fichier:
awk -v x=10 'NR==x{exit 1}' ./somefilename
Awk est une langue entière en soi, conçue pour gérer des fichiers texte délimités tels que les CSV. Les conditions et commandes Awk (qui sont contenues entre des guillemets simples et commencent par les lettres NR
) sont exécutées pour chaque ligne d'un fichier texte. (Boucle implicite.)
Pour apprendre pleinement Awk, je recommande fortement le Tutoriel Grymoire , mais je vais expliquer les fonctionnalités Awk utilisées dans la commande ci-dessus.
Le -v
drapeau à Awk nous permet de définir une variable Awk (une fois) avant que les commandes Awk ne soient exécutées (pour chaque ligne du fichier.) Dans ce cas, nous définissons x
sur 10
.
NR
est une variable particulière Awk se référant à la « N mbre de courant R ECORD. » En d'autres termes, c'est le numéro de ligne que nous regardons dans un passage particulier à travers la boucle.
(Notez qu'il est possible, bien que peu commun, d'utiliser un autre « R ECORD S eparator » que la valeur par défaut d'un retour à la ligne, par le réglage RS
. Voici un exemple de jouer avec des séparateurs record. )
Les scripts awk en général se composent de conditions (en dehors des accolades) combinées avec des actions (à l'intérieur des accolades.) Il peut y avoir des conditions composées et des actions composées, et il y a une condition par défaut (true) et une action par défaut (print), mais nous n'avons pas besoin t'embête pas avec ça.
La condition ici est: "Est-ce la 10ème ligne?" Si tel est le cas, nous quittons avec un état de sortie différent de zéro, ce qui dans le script shell signifie «terminaison de commande infructueuse».
Ainsi, la seule façon dont cette commande Awk se terminera avec succès est si la fin du fichier est atteinte avant que la 10e ligne ne soit atteinte.
Donc, si le script Awk se termine avec succès, cela signifie que vous avez un fichier de moins de dix lignes.
Le prochain -exec
appel (si vous supprimez le echo
) supprimera chaque fichier (qui ira aussi loin dans l'évaluation des find
primaires de) en exécutant:
rm -f ./somefilename
En supposant une find
implémentation qui prend en charge le -readable
prédicat (si votre find
ne le prend pas en charge, supprimez-le simplement, vous obtiendrez simplement des messages d'erreur pour les fichiers non lisibles ou remplacez-les par -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 {} +
Retirez le echo
si heureux.
Ce n'est pas particulièrement efficace en ce qu'elle compte toutes les lignes dans chaque fichier alors qu'il n'a besoin que d'arrêter au x
th un et il court un wc
(et peut -être une rm
) commande pour chaque fichier.
Avec GNU awk
, vous pouvez le rendre beaucoup plus efficace avec:
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
(encore une fois, retirez echo
lorsque vous êtes heureux).
La même chose avec perl
:
x=10 find . -type f -readable -exec perl -Tlne '
if ($. == $ENV{x}) {close ARGV}
elsif (eof) {print $ARGV; close ARGV}' {} +
Remplacez print
par unlink
si heureux.
sh
? 2. Estwc -l < "$file"
plus rapide quewc -l "$file"
? 3. Comment sh connaît-il la valeur de$x
, qui est définie dans le shell appelant Bash?sh
est ce qui se passe dans ce script en ligne$0
, à utiliser pour les messages d'erreur par exemple.wc -l "$file"
afficherait le nom de fichier dont nous ne voulons pas ici et s'exécuteraitwc
même si le fichier ne peut pas être ouvert.$x
est exporté versfind
(x=10 find...
) qui lui-même le transmetsh
.find: -readable: unknown primary or operator
.bash
.bash
est juste un interpréteur de ligne de commande, mais de l'find
implémentation.-readable
est une extension GNU, non disponible sous OS / Xfind
. Il est uniquement utilisé pour limiter les fichiers lisibles (vous ne pourrez pas obtenir le nombre de lignes pour les fichiers non lisibles). Vous pouvez l'omettre pour le premier, vous obtiendrez alors simplement des messages d'erreur lors de l'ouverture des fichierswc
pour les fichiers qui ne sont pas lisibles.Par souci d'exhaustivité, à part AWK, vous pouvez également utiliser GNU sed pour obtenir le même résultat:
Ce qui donne une ligne de commande un peu plus concise.
Explication
la source