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 amit 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 cddas 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 aDefinieren des Arrays explizit anzugeben . Beispielsweise:
LC_ALL=C a=(*)
Sie können herausfinden, in welchem Gebietsschema Sie sich gerade befinden, indem Sie den localeBefehl 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 OnDateinamen 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 cpBefehl anhängen , um Dateien zu behandeln, deren Namen -ordnungsgemäß beginnen.
cp -- *(.On[1,4]) ~
Fügen Sie das DQualifikationsmerkmal 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 bashangebotenen 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 cpArgumente 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 cpersten 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 shiftpassiert 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 5Fall verschiebt es das 1Weg, aber in einem set 1 2 3Fall 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 sedalle 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.
lsAusgaben 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"
findSie 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