find -exec im Bash-Skript mit variabler Erweiterung

14

Ich versuche, einen Befehl auszuführen, der dem folgenden in einem Bash-Skript ähnlich ist. Es sollte alle Unterordner von durchsuchen $sourcedirund alle Dateien eines bestimmten Typs in das Stammverzeichnis von kopieren $targetdir.

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;

Das scheint ziemlich nahe zu sein, außer , dass {}nicht in so geführt wird , $2um-exec sh -c ...

Ich möchte dies so nah wie möglich am "richtigen Weg" tun, mit Toleranz für Sonderzeichen in Dateinamen (insbesondere einfache Anführungszeichen).

Bearbeiten: Ich sehe Leute, die die Verwendung xargsoder Verkettung von Argumenten vorschlagen . Ich hatte den Eindruck, dass dies nur für eine begrenzte Anzahl von Argumenten in Ordnung ist. Wenn ich zum Beispiel Tausende von JPG-Dateien habe, die ich aus einer Reihe von Galerieverzeichnissen in ein riesiges Diashow-Verzeichnis zu kopieren versuche, funktionieren die Argumente für die Verkettung von Lösungen dann immer noch?

Edit 2: Mein Problem war, dass mir eine _vor meiner ersten Option fehlte, um in den -execBefehl zu wechseln . Für alle, die neugierig sind, wie der Befehl find funktioniert, fügen Sie den Befehl _und alles wird gut:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;

Ich habe die Antwort unten zwar akzeptiert, weil sie dieselbe Aufgabe erfüllt, aber effizienter und eleganter ist.

Matthew
quelle
4
Genau aus diesem Grund xargswurde jemals entwickelt, um große Mengen von Argumenten bei regulären Befehlen mit Einschränkungen automatisch zu verarbeiten. Zu beachten ist auch, dass die meisten der maximalen Argumentgrenzen für die Standard-GNU-Utils erheblich verbessert wurden. Sie werden auch einen Leistungsvorteil sehen, indem Sie alle diese Prozessgabeln vermeiden, die für Tausende von Dateien relevant sind.
JM Becker
Mit gnu-find und + anstelle von ";" können Sie auch mit find mehrere Argumente gleichzeitig behandeln. Und Sie sparen das komplizierte Argument, das mit -print0 übergeben wird.
Benutzer unbekannt
@userunknown: Ich antworte auf diese Frage unter Ihrer Antwort.
JM Becker
@user unknown Nun, ich LIEBE diesen Code. Es ist mindestens vollständig POSIX-konform und funktioniert überhaupt ohne GNU-Zeug auf der Maschine. Es gibt Zeiten, in denen Sie dies benötigen, insbesondere auf Servern bei der Arbeit.
Syntaxfehler

Antworten:

6

Sie möchten Dateien eines bestimmten Typs in ein bestimmtes Verzeichnis kopieren? Dies ist am besten mit xargs, und Sie brauchen das nicht einmal sh. Dies ist ein geeigneterer Weg, um es zu tun, sollte auch effizienter laufen.

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir

Wenn Sie spezielle Dateinamen benötigen, verwenden Sie diese NULLals Trennzeichen

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"
JM Becker
quelle
1
Für den zweiten Fall, vergessen Sie nicht , hinzuzufügen , -print0zu findund -0zuxargs
SiegeX
@SiegeX, habe das schon gemacht, bevor mir dein Kommentar aufgefallen ist.
JM Becker
Auch NULList es nicht notwendig, wenn Sie verwenden '{}', es ist wichtig, wenn Sie es nicht tun. Der eigentliche Vorteil zwischen den beiden ist, POSIXabgesehen von der Einhaltung, die Leistung.
JM Becker
6

Sie müssen {}der Shell ein Argument übergeben und dann die einzelnen Argumente durchlaufen.

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +

Hinweis : So funktioniert es, dass das erste Argument für eine Shell der Name der Shell ist. Sie können dies ausnutzen, indem Sie den Namen als übergeben $targetdirund dann den speziellen Parameter $0im Shell-Skript verwenden, um auf dieses Zielverzeichnis zuzugreifen.

SiegeX
quelle
1
"$targetdir"wird nicht in einfache Anführungszeichen gesetzt.
Enzotib
5

Wenn Sie nicht an die Kirche von Xargs glauben:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +

Erläuterung:

cp -t a b c d 

kopiert b, c und d in das Zielverzeichnis a.

-exec cmd {} +

Ruft den Befehl für eine große Anzahl von Dateien gleichzeitig und nicht nacheinander auf (was Standard ist, wenn Sie ";"anstelle von verwenden +). Deshalb muss das targetdir nach vorne gezogen und explizit als target markiert werden.

Dies funktioniert für gnu-find und möglicherweise nicht für andere find-Implementierungen. Natürlich hängt es auch von der -t-Flagge ab.

TechZilla hat natürlich insofern recht, als shes nicht nötig ist, um cp aufzurufen.

Wenn Sie nicht verwenden xargs, was in Kombination mit die meiste Zeit unnötig ist find, können Sie das -print0und- -0Flag nicht lernen .

Benutzer unbekannt
quelle
1
Obvisouly, welche Methode jemand bevorzugen könnte, ist etwas subjektiv. Nach alledem gibt es Gründe, es noch zu verwenden xargs. Das größte Beispiel: Was ist, wenn Sie keine Dateien finden? Ich benutze xargs anstelle von General forLoops, der findUmfang ist viel kleiner. findUnterstützt außerdem nur, dass +GNU findin POSIX nicht definiert ist. Also, während Sie sich frei fühlen sollten, es findalleine zu bevorzugen , tut es nicht alles, was xargs tut. Wenn Sie GNU betrachten xargs, bekommen Sie auch, -Pwas Multi-Core macht. Es lohnt sich trotzdem zu lernen xargs.
JM Becker
Was die Subjektivität anbelangt, so ist meine Meinung offensichtlich unterschiedlich. Objektiv gesehen ist Ihre Antwort auch richtig. Es ist eine der wenigen besten verfügbaren Lösungen.
JM Becker
@TechZilla: Ich hoffe, dass ich daran denke, diese Site erneut zu besuchen, wenn find anfängt, parallele Aufrufe zu unterstützen. :) In den meisten Fällen ist beim Kopieren / Verschieben die Geschwindigkeit der Festplatte der begrenzende Faktor, SSDs können jedoch das Bild verändern. Sie haben Recht, dass eine Nicht-GNU-Lösung eine gute Sache ist. Ansonsten ist die Find-Lösung objektiv kürzer, einfacher und verwendet nur zwei Prozesse.
Benutzer unbekannt
0
read -p "SOURCE: " sourcedir
read -p "TYPE: " type
read -p "TARGET: " targetdir
find -L $sourcedir -iname "*.$type" -exec cp -v {} $targetdir \;
Berthad33
quelle
3
Erwägen Sie, eine Erklärung Ihrer Lösung hinzuzufügen.
HalosGhost