Was ist eine schnelle und nicht zu komplizierte Methode, um alle Dateien in einem Verzeichnis, die weniger als x Zeilen lang sind, in Bash zu löschen?
Hier ist eine POSIX-Lösung, die ziemlich einfach zu verstehen sein sollte:
find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;
Entfernen Sie wie in Stephanes Antwort das , echo
wenn Sie mit dem, was entfernt werden soll, zufrieden sind.
Der Punkt .
repräsentiert das aktuelle Verzeichnis. find
findet Dateien und Verzeichnisse rekursiv darin .
und kann Dinge damit tun.
-type
eines von find
‚s Vorwahlen ; Es ist ein Test, der für jede Datei und jedes Verzeichnis durchgeführt wird, die rekursiv (innerhalb .
) gefunden werden, und der Rest der Primärdaten in der Zeile wird nur ausgewertet, wenn dies zu "true" führt.
In diesem speziellen Fall fahren wir nur fort, wenn es sich um eine reguläre Datei handelt , nicht um ein Verzeichnis oder etwas anderes (z. B. ein Blockgerät).
Der -exec
primäre (von find
) ruft einen externen Befehl auf und fährt nur mit dem nächsten primären Befehl fort, wenn der externe Befehl erfolgreich beendet wurde (Beendigungsstatus "0"). Das {}
wird durch den Dateinamen ersetzt, der vom find
Befehl "berücksichtigt" wird . Der erste -exec
Aufruf entspricht also dem folgenden Shell-Befehl, der nacheinander für jede Datei ausgeführt wird:
awk -v x=10 'NR==x{exit 1}' ./somefilename
Awk ist eine ganze Sprache für sich, die für den Umgang mit begrenzten Textdateien wie CSVs entwickelt wurde. Die Awk-Bedingungen und -Befehle (die zwischen einfachen Anführungszeichen stehen und mit den Buchstaben beginnen NR
) werden für jede Zeile einer Textdatei ausgeführt. (Implizite Schleife.)
Um Awk vollständig zu lernen, empfehle ich das Grymoire-Tutorial , aber ich werde die Awk-Funktionen erläutern, die im obigen Befehl verwendet werden.
Das -v
Flag für Awk ermöglicht es uns, eine Awk-Variable (einmal) zu setzen, bevor die Awk-Befehle ausgeführt werden (für jede Zeile der Datei). In diesem Fall setzen wir x
auf 10
.
NR
ist eine spezielle Variable Awk auf den Bezug „ N umber der aktuellen R ECORD.“ Mit anderen Worten, es ist die Zeilennummer, die wir in einem bestimmten Durchgang durch die Schleife betrachten.
(Beachten Sie, dass es ist möglich, wenn auch ungewöhnlich, einen anderen „zu verwenden , R ecord S eparator“ als die Standardeinstellung eines Newline - Zeichen, durch Einstellung RS
. Hier ist ein Beispiel mit einem Rekord Separatoren zu spielen. )
Awk-Skripte bestehen im Allgemeinen aus Bedingungen (äußere geschweifte Klammern) in Kombination mit Aktionen (innere geschweifte Klammern). Es kann zusammengesetzte Bedingungen und zusammengesetzte Aktionen geben, und es gibt eine Standardbedingung (true) und eine Standardaktion (print), die wir jedoch nicht benötigen kümmere dich nicht darum.
Die Bedingung hier ist: "Ist dies die 10. Zeile?" Wenn dies der Fall ist, beenden wir mit einem Exit-Status ungleich Null, was in Shell-Skripten "erfolglose Befehlsbeendigung" bedeutet.
Daher kann dieser Awk-Befehl nur dann erfolgreich beendet werden, wenn das Ende der Datei erreicht ist, bevor die 10. Zeile erreicht ist.
Wenn das Awk-Skript erfolgreich beendet wird, bedeutet dies, dass Sie eine Datei mit weniger als zehn Zeilen haben.
Der nächste -exec
Aufruf (wenn Sie das entfernen echo
) entfernt jede Datei (die bei der Auswertung der find
Primärdaten so weit kommt), indem Folgendes ausgeführt wird:
rm -f ./somefilename
Angenommen, eine find
Implementierung, die das -readable
Prädikat unterstützt (wenn Sie find
es nicht unterstützen, entfernen Sie es einfach, Sie erhalten nur Fehlermeldungen für nicht lesbare Dateien oder ersetzen durch -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 {} +
Entfernen Sie die, echo
wenn glücklich.
Dies ist nicht besonders effizient, da alle Zeilen in jeder Datei gezählt werden, während nur die x
dritte angehalten werden muss und für jede Datei ein wc
(und möglicherweise ein rm
) Befehl ausgeführt wird.
Mit GNU awk
können Sie es viel effizienter machen mit:
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
(wieder entfernen, echo
wenn glücklich).
Das gleiche mit perl
:
x=10 find . -type f -readable -exec perl -Tlne '
if ($. == $ENV{x}) {close ARGV}
elsif (eof) {print $ARGV; close ARGV}' {} +
Ersetzen Sie print
durch, unlink
wenn Sie glücklich sind.
sh
? 2. Istwc -l < "$file"
schneller alswc -l "$file"
? 3. Woher kennt sh den Wert von$x
, der in der aufrufenden Bash-Shell definiert ist?sh
ist, was in diesem Inline-Skript steht$0
, um zum Beispiel für Fehlermeldungen verwendet zu werden.wc -l "$file"
würde den Dateinamen drucken, den wir hier nicht wollen, und würdewc
auch dann ausgeführt , wenn die Datei nicht geöffnet werden kann.$x
wird nachfind
(x=10 find...
) exportiert, das es selbst an weitergibtsh
.find: -readable: unknown primary or operator
.bash
.bash
ist nur ein Befehlszeileninterpreter, aber derfind
Implementierung.-readable
ist eine GNU-Erweiterung, die in OS / X nicht verfügbar istfind
. Es wird nur verwendet, um sich auf die lesbaren Dateien zu beschränken (Sie könnten die Zeilenanzahl für nicht lesbare Dateien nicht ermitteln). Sie können es für das erste weglassen. Beim Öffnen der Dateienwc
für die nicht lesbaren Dateien werden dann nur Fehlermeldungen angezeigt.Der Vollständigkeit halber können Sie neben AWK auch GNU sed verwenden, um das gleiche Ergebnis zu erzielen:
Dies führt zu einer etwas präziseren Befehlszeile.
Erläuterung
quelle