Frage

Was wäre auf der UNIX-Bash-Shell (insbesondere Mac OS X Leopard) der einfachste Weg, jede Datei mit einer bestimmten Erweiterung aus einer Ordnerhierarchie (einschließlich Unterverzeichnissen) in denselben Zielordner (ohne Unterordner) zu kopieren?

Offensichtlich besteht das Problem, dass es in der Quellhierarchie Duplikate gibt.Es würde mir nichts ausmachen, wenn sie überschrieben würden.

Beispiel:Ich muss jede TXT-Datei in der folgenden Hierarchie kopieren

/foo/a.txt
/foo/x.jpg
/foo/bar/a.txt
/foo/bar/c.jpg
/foo/bar/b.txt

In einen Ordner namens „dest“ und holen Sie sich:

/dest/a.txt
/dest/b.txt
War es hilfreich?

Lösung

Im Bash:

find /foo -iname '*.txt' -exec cp \{\} /dest/ \;

find findet alle Dateien unter dem Pfad /foo passend zum Platzhalter *.txt, ohne Berücksichtigung der Groß- und Kleinschreibung (Das ist was -iname bedeutet).Für jede Datei find wird ausgeführt cp {} /dest/, mit der gefundenen Datei anstelle von {}.

Andere Tipps

Das einzige Problem bei der Lösung von Magnus besteht darin, dass sie für jede Datei einen neuen „cp“-Prozess auslöst, was insbesondere bei einer großen Anzahl von Dateien nicht besonders effizient ist.

Unter Linux (oder anderen Systemen mit GNU-Coreutils) können Sie Folgendes tun:

find . -name "*.xml" -print0 | xargs -0 echo cp -t a

(Mit -0 funktioniert es, wenn Ihre Dateinamen seltsame Zeichen – wie Leerzeichen – enthalten.)

Leider glaube ich, dass Macs mit BSD-ähnlichen Tools ausgestattet sind.Kennt jemand ein „Standard“-Äquivalent zum „-t“-Schalter?

Die obigen Antworten lassen keine Namenskollisionen zu, da der Fragesteller nichts dagegen hatte, dass Dateien überschrieben werden.

Da es mir etwas ausmacht, wenn Dateien überschrieben werden, habe ich mir einen anderen Ansatz ausgedacht.Ersetzen Sie jedes / im Pfad durch – behalten Sie die Hierarchie in den Namen bei und legen Sie alle Dateien in einem flachen Ordner ab.

Wir verwenden find, um die Liste aller Dateien abzurufen, dann awk, um einen mv-Befehl mit dem ursprünglichen Dateinamen und dem geänderten Dateinamen zu erstellen, und übergeben diese dann zur Ausführung an Bash.

find ./from -type f | awk '{ str=$0; sub(/\.\//, "", str); gsub(/\//, "-", str); print "mv " $0 " ./to/" str }' | bash

Dabei sind ./from und ./to Verzeichnisse für mv from und to.

Wenn Sie wirklich nur einen Befehl ausführen möchten, warum laden Sie dann nicht einen auf und führen ihn aus?Etwa so:

$ find /foo  -name '*.txt' | xargs echo | sed -e 's/^/cp /' -e 's|$| /dest|' | bash -sx

Aber das wird in Bezug auf die Leistung keine allzu große Rolle spielen, es sei denn, Sie machen das häufig oder haben eine Menge Dateien.Seien Sie jedoch vorsichtig bei Namensabsprachen.Beim Testen ist mir aufgefallen, dass GNU cp zumindest vor Kollisionen warnt:

cp: will not overwrite just-created `/dest/tubguide.tex' with `./texmf/tex/plain/tugboat/tubguide.tex'

Ich denke, das sauberste ist:

$ find /foo  -name '*.txt' | xargs -i cp {} /dest

Es muss weniger Syntax beachtet werden als bei der Option -exec.

Was die Manpage für cp auf einer FreeBSD-Box betrifft, ist kein -t-Schalter erforderlich.cp geht davon aus, dass das letzte Argument in der Befehlszeile das Zielverzeichnis ist, wenn mehr als zwei Namen übergeben werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top