Kopieren Sie Dateien aus mehreren Verzeichnissen, wenn ihr Name in einer Liste erscheint, in ein einziges Verzeichnis

Kopieren Sie Dateien aus mehreren Verzeichnissen, wenn ihr Name in einer Liste erscheint, in ein einziges Verzeichnis

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.txtin einen einzigen Ordner kopieren, z. B. . Für jede Hilfe bin ich sehr dankbar! Ich nehme an, dass es und destination_folderbetrifft , aber ich scheine nicht damit klarzukommen,findcp

Antwort1

Wird einmal pro Dateinamen ausgeführt findund 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, findum jede reguläre Datei mit diesem Namen zu finden. Das Zielverzeichnis wird explizit vermieden, indem findverwendet 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_folderwird 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, bashda 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 cpAufrufe durchführen zu müssen und nicht nur einmal pro gefundener Datei.cp

Der obige Code erstellt ein Array, namesdas 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 cpes mit der Option -boder --backup.

Auf Nicht-Linux-Systemen cpist GNU häufig verfügbar, gcpnachdem 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

verwandte Informationen