Ich habe einen riesigen Ordnerbaum, jeder mit mehreren Unterverzeichnissen, die bis zu drei Ebenen reichen. Hier ist ein Beispiel mit nur einer Ebene:
$ tree
.
|-- AB.txt
|-- CD.txt
|-- destination_folder
|-- spreadsheet.txt
`-- subdirectory
`-- EF.txt
2 directories, 4 files
Ich habe eine Liste mit Dateinamen, die mich interessieren spreadsheet.txt
:
$ cat spreadsheet.txt
AB.txt
CD.txt
EF.txt
Ich möchte alle Dateien, die in erscheinen, spreadsheet.txt
in einen einzigen Ordner kopieren, z. B. . Für jede Hilfe bin ich sehr dankbar! Ich nehme an, dass es und destination_folder
betrifft , aber ich scheine nicht damit klarzukommen,find
cp
Antwort1
Wird einmal pro Dateinamen ausgeführt find
und geht davon aus, dass kein Dateiname eingebettete Zeilenumbrüche enthält:
#!/bin/sh
mkdir -p destination_folder || exit 1
while IFS= read -r name; do
find . -path ./destination_folder -prune -o \
-type f -name "$name" -exec cp {} destination_folder \;
done <spreadsheet.txt
Dies erstellt zuerst das Zielverzeichnis im aktuellen Verzeichnis (und wird beendet, wenn dies fehlschlägt). Anschließend liest es die Eingabedatei Zeile für Zeile und ruft auf, find
um jede reguläre Datei mit diesem Namen zu finden. Das Zielverzeichnis wird explizit vermieden, indem find
verwendet wird -prune
, wann immer wir bei unserer Suche darauf stoßen.
Sobald eine Datei mit dem richtigen Namen gefunden wird, wird sie in das Zielverzeichnis kopiert. Wenn mehrere Dateien den gleichen Namen haben, destination_folder
wird die Kopie überschrieben.
Wenn das aktuelle Verzeichnis sehr groß ist oder die Liste der Dateinamen lang ist (aber nicht viele tausend Zeilen), dann wird dies eine langsame Operation sein. Wir können uns daher für Folgendes entscheiden:einzelAufruf von find
. Der folgende Code setzt voraus, dass er mit eg ausgeführt wird, bash
da er Arrays verwendet:
#!/bin/bash
mkdir -p destination_folder || exit 1
names=()
while IFS= read -r name; do
names+=( -o -name "$name" )
done <spreadsheet.txt
find . -path ./destination_folder -prune -o \
\( "${names[@]:1}" \) -type f -exec cp -t destination_folder {} +
Hier habe ich mich auch für die Verwendung cp -t
(einer GNU- Erweiterung) entschieden, um möglichst wenige cp
Aufrufe durchführen zu müssen und nicht nur einmal pro gefundener Datei.cp
Der obige Code erstellt ein Array, names
das schließlich das richtige Format für die Verwendung mit hat find
. Der Befehl, der am Ende des obigen Codes tatsächlich ausgeführt wird, lautet im Beispiel Ihrer Frage:
find . -path ./destination_folder -prune -o '(' -name AB.txt -o -name CD.txt -o -name EF.txt ')' -type f -exec cp -t destination_folder '{}' +
Um das Problem von Dateinamenkollisionen im Zielverzeichnis zu vermeiden, wenn Sie GNU verwenden cp
(z. B. auf einem Linux-System), verwenden Sie cp
es mit der Option -b
oder --backup
.
Auf Nicht-Linux-Systemen cp
ist GNU häufig verfügbar, gcp
nachdem GNU Coreutils über einen Paketmanager installiert wurde.
Das letzte Skript, aber für /bin/sh
(keine Arrays):
#!/bin/sh
mkdir -p destination_folder || exit 1
set --
while IFS= read -r name; do
set -- -o -name "$name" "$@"
done <spreadsheet.txt
shift
find . -path ./destination_folder -prune -o \
\( "$@" \) -type f -exec cp -t destination_folder {} +
Antwort2
Eine einfache for-Schleife:
for filename in $(cat spreadsheet.txt)
do
find . -name "$filename" -exec cp {} /destination/folder \;
done