Ich habe viele Dateien, die nach Dateinamen in einem Verzeichnis sortiert sind. Ich möchte die letzten N (N = 4) Dateien in mein Home-Verzeichnis kopieren. Wie soll ich das machen
cp ./<the final 4 files> ~/
Ich habe viele Dateien, die nach Dateinamen in einem Verzeichnis sortiert sind. Ich möchte die letzten N (N = 4) Dateien in mein Home-Verzeichnis kopieren. Wie soll ich das machen
cp ./<the final 4 files> ~/
Antworten:
Dies kann einfach mit bash / ksh93 / zsh-Arrays durchgeführt werden:
a=(*)
cp -- "${a[@]: -4}" ~/
Dies funktioniert für alle nicht ausgeblendeten Dateinamen, auch wenn sie Leerzeichen, Tabulatoren, Zeilenumbrüche oder andere schwierige Zeichen enthalten (vorausgesetzt, das aktuelle Verzeichnis enthält mindestens 4 nicht ausgeblendete Dateien mit bash
).
a=(*)
Dadurch wird ein Array a
mit allen Dateinamen erstellt. Die von bash zurückgegebenen Dateinamen sind alphabetisch sortiert. (Ich gehe davon aus, dass dies das ist, was Sie mit "sortiert nach Dateinamen" meinen . )
${a[@]: -4}
Dies gibt die letzten vier Elemente des Arrays zurück a
(vorausgesetzt, das Array enthält mindestens 4 Elemente mit bash
).
cp -- "${a[@]: -4}" ~/
Dadurch werden die letzten vier Dateinamen in Ihr Ausgangsverzeichnis kopiert.
Dadurch werden nur die letzten vier Dateien in das Ausgangsverzeichnis a_
kopiert und gleichzeitig die Zeichenfolge vor den Namen der kopierten Datei gestellt:
a=(*)
for fname in "${a[@]: -4}"; do cp -- "$fname" ~/a_"$fname"; done
Wenn wir a=(./some_dir/*)
anstelle von verwenden a=(*)
, tritt das Problem auf, dass das Verzeichnis an den Dateinamen angehängt wird. Eine Lösung ist:
a=(./some_dir/*)
for f in "${a[@]: -4}"; do cp "$f" ~/a_"${f##*/}"; done
Eine andere Lösung besteht darin, eine Subshell und cd
das Verzeichnis in der Subshell zu verwenden:
(cd ./some_dir && a=(*) && for f in "${a[@]: -4}"; do cp -- "$f" ~/a_"$f"; done)
Nach Abschluss der Subshell kehrt die Shell zum ursprünglichen Verzeichnis zurück.
Die Frage fragt nach Dateien "sortiert nach Dateinamen". Diese Reihenfolge wird, wie Olivier Dulac in den Kommentaren ausführt, von Land zu Land unterschiedlich sein. Wenn es wichtig ist, dass die Ergebnisse unabhängig von den Computereinstellungen festgelegt werden, empfiehlt es sich, das Gebietsschema beim a
Definieren des Arrays explizit anzugeben . Beispielsweise:
LC_ALL=C a=(*)
Sie können herausfinden, in welchem Gebietsschema Sie sich gerade befinden, indem Sie den locale
Befehl ausführen .
Wenn Sie verwenden zsh
, können Sie ()
eine Liste sogenannter Glob-Qualifizierer in Klammern einfügen, die die gewünschten Dateien auswählen. In deinem Fall wäre das
cp *(On[1,4]) ~/
Hier werden On
Dateinamen in umgekehrter Reihenfolge alphabetisch sortiert und [1,4]
nur die ersten 4 von ihnen verwendet.
Sie können dies robuster machen, indem Sie nur einfache Dateien (ohne Verzeichnisse, Pipes usw.) mit auswählen .
und auch --
den cp
Befehl anhängen , um Dateien zu behandeln, deren Namen -
ordnungsgemäß beginnen.
cp -- *(.On[1,4]) ~
Fügen Sie das D
Qualifikationsmerkmal hinzu, wenn Sie auch versteckte Dateien (Punktdateien) berücksichtigen möchten:
cp -- *(D.On[1,4]) ~
*([-4,-1])
.
on
) das Standardverhalten ist, daher muss dies nicht angegeben werden, und -
vor den Zahlen werden die Dateinamen von unten gezählt.
Hier ist eine Lösung mit extrem einfachen Bash-Befehlen.
find . -maxdepth 1 -type f | sort | tail -n 4 | while read -r file; do cp "$file" ~/; done
Erläuterung:
find . -maxdepth 1 -type f
Findet alle Dateien im aktuellen Verzeichnis.
sort
Sortiert alphabetisch.
tail -n 4
Nur die letzten 4 Zeilen anzeigen.
while read -r file; do cp "$file" ~/; done
Durchläuft jede Zeile, die den Kopierbefehl ausführt.
Solange Ihnen die Shell-Sortierung zusagt, können Sie Folgendes tun:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir
Dies ist der bash
angebotenen Array-spezifischen Lösung sehr ähnlich , sollte jedoch auf jede POSIX-kompatible Shell portierbar sein. Einige Anmerkungen zu beiden Methoden:
Es ist wichtig, dass Sie Ihre cp
Argumente anführen, cp --
oder Sie erhalten entweder .
einen Punkt oder /
den Kopf jedes Namens. Wenn Sie dies nicht tun, riskieren Sie einen führenden -
Gedankenstrich im cp
ersten Argument, der als Option interpretiert werden kann und dazu führt, dass die Operation fehlschlägt oder auf andere Weise unbeabsichtigte Ergebnisse liefert.
set -- ./*
oder array=(./*)
.Wenn Sie beide Methoden verwenden, ist es wichtig, dass Sie mindestens so viele Elemente in Ihrem arg-Array haben, wie Sie zu entfernen versuchen - ich mache das hier mit einer mathematischen Erweiterung. Das shift
passiert nur, wenn das arg-Array mindestens 4 Elemente enthält - und verschiebt dann nur die ersten Argumente, die einen Überschuss von 4 ergeben.
set 1 2 3 4 5
Fall verschiebt es das 1
Weg, aber in einem set 1 2 3
Fall verschiebt es nichts.a=(1 2 3); echo "${a[@]: -4}"
Druckt eine leere Zeile.Wenn Sie von einem Verzeichnis in ein anderes kopieren, können Sie Folgendes verwenden pax
:
set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir
... die sed
alle Dateinamen beim Kopieren durch einen -Stil ersetzen würde.
Wenn es nur Dateien gibt, deren Namen keine Leerzeichen oder Zeilenumbrüche (und die Sie nicht geändert haben $IFS
) oder Glob-Zeichen (oder einige nicht druckbare Zeichen mit einigen Implementierungen von ls
) enthalten und nicht mit beginnen .
, können Sie dies tun :
cp -- $(ls | tail -n 4) ~/
a_
, ALLEN vier Dateien schnell ein Präfix voran zu stellen? Ich habe es versucht echo "a_$(ls | tail -n 4)"
, aber es geht nur der ersten Datei voran.
ls
Ausgaben als fehlerhafte Form angesehen, da bei Dateinamen, die nicht nur Leerzeichen, sondern auch Tabulatoren, Zeilenumbrüche oder andere gültige, aber "schwierige" Zeichen enthalten, normalerweise ein schrecklicher Fehler auftritt.
Dies ist eine einfache Bash-Lösung ohne Schleifencode. Kopieren Sie die letzten 4 Dateien vom aktuellen Verzeichnis zum Ziel:
$ find . -maxdepth 1 -type f |tail -n -4|xargs cp -t "$destdir"
find
Sie die Dateien in der gewünschten Reihenfolge erhalten? Wie stellen Sie sicher, dass Dateien mit Leerzeichen (einschließlich Zeilenumbrüchen) im Namen korrekt behandelt werden?
LC_ALL=C