Sortieren Sie Dateien nach der höchsten Nummer im Dateinamen

Sortieren Sie Dateien nach der höchsten Nummer im Dateinamen

Ich habe einen Haufen Dateien, die alle so heißen:

name_file-1.txt
name_file-2.txt
name_file-3.txt
some_other_file-1.txt
some_other_file-2.txt

Es gibt Tausende verschiedener Dateinamen, manche mit nur einem -1.txtam Ende, manche mit -1.txt, -2.txt-60.txt

Ich muss die höchsten Nummern jeder Datei kopieren, also name_file-3.txt, some_other_file-2.txt. Wie mache ich das in einer Linux-Befehlszeile?

Antwort1

Mit zsh:

typeset -A greatest
for f (*-*(n)) greatest[${f%-*}]=$f
cp -- $greatest /destination
  • *-*(n): nicht versteckte Dateien, deren Name ein -( *-*) enthält, numerisch sortiert ( (n)Glob-Qualifizierer).
  • ${f%-*}: Teil des Dateinamens ganz rechts -(oder bis zum Ende, wenn kein vorhanden ist -).
  • $greatest: expandiert zum nicht leerenWerteder assoziativen Arrays. Daher wird hier bei Dateien mit derselben Wurzel nur die Datei mit der größten Nummer erweitert.

Antwort2

files=(*)
mapfile -t prefixes < <(printf "%s\n" "${files[@]%-*}" | sort -u)
for p in "${prefixes[@]}"; do ls -v "$p"* | tail -1; done
name_file-3.txt
some_other_file-2.txt

Und diese dann in ein anderes Verzeichnis kopieren:

for ...; done | xargs cp -t /destination/directory

Antwort3

Wenn sich die Dateien im aktuellen Arbeitsverzeichnis befinden und ihre Namen den Beispielen entsprechen (ein einzelner Bindestrich vor einer Zahl), sollte die folgende POSIX-kompatible Pipeline funktionieren:

ls | sort -t- -k1,1 -k2,2rn | awk -F- 'k!=$1 {print; k=$1}' | pax -rw /path/to/dir

Die awk-Komponente kann durch ein sort -u ersetzt werden, wenn die Option -u von sort stabil ist (so dass die erste Zeile eines Satzes immer zur Darstellung dieses Satzes gewählt wird). POSIX erfordert diese Stabilität nicht, aber laut ihren Handbüchern bieten die Implementierungen von {Free,Net,Open}BSD und GNU sie. Wenn Sie gerne das Schicksal herausfordern:

ls | sort -t- -k1,1 -k2,2rn | sort -mut- -k1,1 | pax -rw /path/to/dir

In beiden Fällen darf sich das Zielverzeichnis nicht im aktuellen Arbeitsverzeichnis befinden.

Antwort4

Ich würde die Datei in tabulatorgetrennte Teile aufteilen, um eine zuverlässigere, anpassbare Dateinamenanalyse zu ermöglichen, und dann mit awk den höchsten Rang jedes Teils ermitteln und einen Bericht erstellen. Probieren Sie zuerst jeden Teil der Pipeline aus, bevor Sie mit dem nächsten fortfahren!

find DIR -type f <other find criteron> -print | 
perl -lne 'print join("\t",(/^(.*?-)(\d+)(\.\w+)$/))' |
awk -F\\t '$2 > f[$1] { f[$1]=$2;e[$1]=$3; } END { for (k in f) { print k f[k] e[k] }}' |
xargs cp -t <desination_directory>

Das awk-Skript fügt jedem Dateinamen einen zugehörigen Array-Eintrag hinzu und behält dabei immer den höchsten gefundenen Rang bei. Die Erweiterung wird in einem eigenen Array gespeichert. Nachdem alle Eingaben verarbeitet wurden, werden alle Array-Einträge ausgegeben, einer pro Zeile. Die xargs cp -tZeile kopiert alle Dateien in das von Ihnen angegebene Verzeichnis.

Es gibt eine andere Methode, diewird nicht funktionierensehr gut, wenn die Zahlen größer als 9 sind und nicht mit 0 aufgefüllt sind. Diese Methode sortiert die Dateien lexikografisch, dann ändert sich beim Parsen der Liste der erste Teil, der zuletzt gesehene Dateiname wird verwendet. Wenn die Dateinamen so sind, funktioniert es nicht:

file-9.txt
file-10.txt

weil file-10.txt vor file-9 erscheint. Das obige awk-Skript führt einen numerischen Vergleich durch.

ACHTUNG: Dateinamen mit Tabulatoren und Zeilenumbrüchen führen zum Absturz.

Vorbehalt 2: Wenn mehrere Erweiterungen pro Dateinamenpräfix möglich sind, müssen wir einige Anpassungen vornehmen, um es richtig zu machen.

verwandte Informationen