So kopieren Sie jede vierte Datei in einen Ordner

15

Ich habe viele Dateien in einem Ordner mit dem Namen 00802_Bla_Aquarium_XXXXX.jpg. Jetzt muss ich jede vierte Datei in einen Unterordner kopieren und sagen, in selected/.

00802_Bla_Aquarium_00020.jpg <= this one
00802_Bla_Aquarium_00021.jpg
00802_Bla_Aquarium_00022.jpg
00802_Bla_Aquarium_00023.jpg
00802_Bla_Aquarium_00024.jpg <= this one
00802_Bla_Aquarium_00025.jpg
00802_Bla_Aquarium_00026.jpg
00802_Bla_Aquarium_00027.jpg
00802_Bla_Aquarium_00028.jpg <= this one
00802_Bla_Aquarium_00029.jpg

Wie mache ich das?

aebersold
quelle
Möglicherweise können Sie die Lösungen für superuser.com/q/396536/87552 für einige Ausgaben von ausführenls . Siehe auch stackoverflow.com/q/4553751/789593, in dem nach jeder zweiten Zeile gefragt wird.
NN
Wenn sie nummeriert sind und Sie die letzte Nummer kennen, können Sie eine Variation vonfor n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done
Ulrich Schwarz

Antworten:

12

Mit zsh können Sie Folgendes tun:

n=0; cp 00802_Bla_Aquarium_?????.jpg(^e:'((n++%4))':) /some/place

POSIXly, dieselbe Idee, nur etwas ausführlicher:

# put the file list in the positional parameters ($1, $2...).
# the files are sorted in alphanumeric order by the shell globbing
set -- 00802_Bla_Aquarium_?????.jpg

n=0
# loop through the files, increasing a counter at each iteration.
for i do
  # every 4th iteration, append the current file to the end of the list
  [ "$(($n % 4))" -eq 0 ] && set -- "$@" "$i"

  # and pop the current file from the head of the list
  shift
  n=$(($n + 1))
done

# now "$@" contains the files that have been appended.
cp -- "$@" /some/place

Da diese Dateinamen keine Leerzeichen oder Platzhalterzeichen enthalten, können Sie auch Folgendes tun:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place
Stéphane Chazelas
quelle
Können Sie bitte Kommentar auch in Posix-Code setzen
Rahul Patil
@ChrisDown for i doist Standard und tatsächlich portabler als for i; dound ist die kanonische Methode, um die Positionsparameter zu durchlaufen.
Stéphane Chazelas
7

In Bash, eine lustige Möglichkeit, die hier ziemlich gut funktioniert:

cp 00802_Bla_Aquarium_*{00..99..4}.jpg selected

Das ist definitiv die kürzeste und effizienteste Antwort: keine Unterschale, keine Schleife, keine Leitung, kein awkexterner Prozess auf der Station; Nur eine Abzweigung zu cp(das können Sie sowieso nicht vermeiden) und eine Bash-Klammer-Erweiterung und Glob (das können Sie insgesamt loswerden, da Sie wissen, wie viele Dateien Sie haben).

gniourf_gniourf
quelle
4
… Und 2 Annahmen: Dateien werden fortlaufend nummeriert und die erste ist mit 4 teilbar.
manatwork 21.12.12
@manatwork kein Problem, wenn das erste nicht durch 4 teilbar ist: zB cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedwenn Sie möchten 3 mod 4. (Ich benutze die wunderbare Tatsache, dass 100 durch 4 teilbar ist). Bei fortlaufend nummerierten Dateien gehen alle anderen Antworten von denselben Ergebnissen aus.
gniourf_gniourf
Welche anderen Antworten setzen eine fortlaufende Nummerierung voraus? Ich fand nur den Kommentar von Ulrich Schwarz und keine solche Antwort.
Handarbeit
5

Mit bash können Sie einfach Folgendes tun:

n=0
for file in ./*.jpg; do
   test $n -eq 0 && cp "$file" selected/
   n=$((n+1))
   n=$((n%4))
done

Das Muster ./*.jpgwird durch eine alphabetisch sortierte Liste von Dateinamen ersetzt, wie vom Bash-Mann angegeben, sodass es Ihrem Zweck entspricht.

jfg956
quelle
In der bash Ihre drei Zeilen innerhalb der forSchleife mit nur dieser Zeile ersetzt werden: (((++n)%4)) || cp "$file" selected/. ;-). Das Problem ist jedoch, dass Sie cpfür jede Datei ein forken, was nicht wirklich effizient ist.
gniourf_gniourf
1

Wenn Sie rubyinstalliert haben, können Sie den folgenden Einzeiler verwenden. Beachten Sie, dass davon ausgegangen wird, dass das ausgewählte Verzeichnis vorhanden ist.

ruby -rfileutils -e 'files = Dir.glob("*.jpg").each_slice(4) { |file| FileUtils.cp(file.first, "selected/" + file.first) }'

Es erstellt eine Liste aller Dateien mit der Erweiterung .jpg im aktuellen Verzeichnis, schneidet sie in Listen mit vier Elementen und kopiert das erste Element aus jeder solchen Liste in das im aktuellen Verzeichnis ausgewählte Verzeichnis.

NN
quelle
1

Wenn Sie wissen, dass Ihre Dateinamen keine Zeilenumbrüche enthalten, können Sie Folgendes verwenden:

find . -maxdepth 1 -name "*.jpg" | sort | while IFS= read -r file; do
  cp "$file" selected/
  IFS= read -r; IFS= read -r; IFS= read -r
done
jfg956
quelle
@ Gilles: danke für die Bearbeitung. Ich habe die Dateivariable in den 3 Lesevorgängen entfernt, da ich darauf hinweisen möchte, dass sie nicht benötigt werden.
jfg956
0

Sie können einen GUI-Dateimanager verwenden und die Größe des Fensterrands ändern, bis alle 5 Dateien eine Zeile belegen. Wählen Sie dann mit der Maus die erste Spalte aus.

John Chain
quelle
-1

Wenn Sie sich stackoverflow.com/q/4553751/789593 ansehen, das eine Lösung zum Kopieren aller Sekunden bietet, wie von @NN erwähnt, ist dies eine sehr einfache Lösung:

  • Entfernen Sie die erste Datei aus dem Startordner
  • Kopieren Sie jede Sekunde aus dem Startordner in einen Zwischenordner
  • Kopieren Sie jede Sekunde aus dem Zwischenordner in den endgültigen Ordner
  • Fügen Sie die erste Datei manuell hinzu

Es ist möglicherweise nicht sehr effizient, da es ein zusätzliches Kopierschrittbit erfordert, das sehr einfach durchzuführen sein sollte.

Dennis Jaheruddin
quelle