2008-08-26 5 views
34

In der UNIX-Bash-Shell (speziell Mac OS X Leopard) wäre das einfachste Verfahren, jede Datei mit einer bestimmten Erweiterung aus einer Ordnerhierarchie (einschließlich Unterverzeichnissen) in denselben Zielordner zu kopieren (ohne Unterordner)?Unix-Shell-Datei-Copy-Flattening-Ordnerstruktur

Offensichtlich gibt es das Problem von Duplikaten in der Quellhierarchie. Ich hätte nichts dagegen, wenn sie überschrieben werden.

Beispiel: Ich brauche jede TXT-Datei in der folgenden Hierarchie

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

Um einen Ordner mit dem Namen 'dest' zu kopieren und erhalten:

/dest/a.txt 
/dest/b.txt 

Antwort

51

In bash:

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

find finden alle Dateien unter dem Pfad /foo passend zum Wildca rd *.txt, case insensitiv (Das ist was -iname bedeutet). Für jede Datei führt find mit der gefundenen Datei anstelle von {} aus.

+3

-exec cp -t dest/{} + wird schneller sein, weil es cp nur einmal mit mehreren Argumenten ausführen muss. -t steht für --target-Verzeichnis. -l kann hier nützlich sein, um Hardlinks zu machen. stattdessen. Und vielleicht -u, um mit der neuesten Version für jeden Dateinamen zu enden, anstatt den ersten zu finden. –

+0

Allgemeine Bash-Frage hier ... ist {} spezifisch zu finden oder ist das ein Weg, einen Piped-Wert darzustellen? –

+0

@BrianBolton '{}' ist spezifisch für 'find' –

13

Das einzige Problem mit Magnus 'Lösung ist, dass es für jede Datei einen neuen "cp" -Prozess ausgibt, der nicht besonders effizient ist, besonders wenn es eine große Anzahl von Dateien gibt.

auf Linux (oder anderen Systemen mit GNU coreutils) können Sie tun:

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

(. Die -0 ermöglicht es zu arbeiten, wenn Ihre Dateinamen seltsame Zeichen haben - wie Plätze - in ihnen)

Leider denke ich Macs kommen mit BSD-style-Tools. Wer kennt einen "Standard", der dem "-t" -Schalter entspricht?

+0

Das einzige Problem mit diesem ist, dass Sie die Befehlszeile überschreiten können Puffer mit Xargs, wenn die Anzahl der Dateien sehr groß ist. –

+0

Nicht wahr. Von der OS X Manpage (aber wahr für jeden verwendeten Unix): "Alle Argumente, die in der Befehlszeile angegeben sind, werden dem Dienstprogramm bei jedem Aufruf übergeben, gefolgt von _some number_ der Argumente, die von der Standardeingabe von xargs gelesen werden Das Dienstprogramm wird _wiederholt ausgeführt_, bis die Standardeingabe erschöpft ist. " (Betonung meiner.) –

+0

Ich glaube, ich sah das Verhalten auf Cygwin vor einiger Zeit, wo es das nicht getan hat. Ich machte dann die Schlussfolgerung, dass das Posix-Verhalten war (mein Fehler, dass ich das Dokument nicht wirklich überprüft habe). –

1

Soweit die man-Seite für cp auf einer FreeBSD-Box geht, gibt es keine Notwendigkeit für eine-t-Schalter. cp nimmt das letzte Argument in der Befehlszeile als Zielverzeichnis an, wenn mehr als zwei Namen übergeben werden.

+0

Der Punkt von -t ist, dass Sie das Ziel als eines der ersten Argumente setzen können. xargs macht es nicht einfach, Argumente in die Mitte zu legen. –

3

Wenn Sie wirklich nur einen Befehl ausführen möchten, warum nicht einen Nachteil und führen Sie es aus? Wie so:

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

aber das wird keine Rolle, zu viel Performance-weise, wenn Sie diese viel tun, oder eine Tonne von Dateien. Achten Sie jedoch auf Namensabsprachen. Ich bemerkte, bei der Prüfung, dass die GNU cp zumindest warnt vor Kollisionen:

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

Ich denke, die saubersten ist:

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

Weniger Syntax als die -exec Option zu erinnern.

10

Die obigen Antworten erlauben keine Namenskollisionen, da der Fragesteller nichts dagegen hatte, dass Dateien überschrieben wurden.

Ich denke, dass Dateien überschrieben werden, so kam mit einem anderen Ansatz. Ersetzen Sie jeden/in den Pfad mit - behalten Sie die Hierarchie in den Namen und legt alle Dateien in einem flachen Ordner.

Wir verwenden find, um die Liste aller Dateien zu erhalten, dann awk, um einen mv-Befehl mit dem ursprünglichen Dateinamen und dem modifizierten Dateinamen zu erstellen und diese an bash zur Ausführung zu übergeben.

wo ./from und ./to sind Verzeichnisse zu mv von und zu.

+2

Dies berücksichtigt nicht ungerade Zeichen in Dateinamen wie Leerzeichen. –

+3

@ NathanJ.Brauer Sie können dies mit vernünftiger Platzierung von \ "in der MV-Befehl am Ende beheben. (Ich habe versucht, die Antwort zu bearbeiten, um dies zu zeigen, aber abgelehnt wurde. Danke Moderatoren.) – Paul