rsync verwendet Regex, um nur einige Dateien einzuschließen

11

Ich versuche, rsync auszuführen, um einige Dateien rekursiv in einen Pfad zu kopieren, basierend auf ihrem Dateinamenmuster, wobei die Groß- und Kleinschreibung nicht berücksichtigt wird . Folgendes habe ich getan, um rsync auszuführen:

$ rsync -avvz --include ='*/' --include='.*[Nn][Aa][Mm][E].*' --exclude='*' ./a/ ./b/

Nichts wird kopiert, die Debug-Ausgabe zeigt:

[sender] hiding file 1Name.txt because of pattern *
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] hiding directory test1 because of pattern *
[sender] hiding file NaMe.txt because of pattern *

Ich habe versucht,: --include='*[Nn][Aa][Mm][E]*'und andere Kombinationen zu verwenden, aber es geht immer noch nicht.

Irgendwelche Ideen, wie man Regex verwendet, um einige Dateien einzuschließen?

user1957413
quelle
4
Warum benutzt du das --exclude='*'?
2
es schließt also alles aus, was nicht Teil des Includes ist.
'Datei 1Name.txt wegen Muster ausblenden ' bedeutet Folgendes: - "Muss diese - Ausschlussregel im Befehl enthalten sein?" oder Wenn Sie einige Dateien ausschließen möchten, warum dann ein " ".
Akshay Patil

Antworten:

5

rsync spricht keinen regulären Ausdruck. Sie können find und grep eintragen, obwohl es ein wenig arkan wird. So finden Sie die Zieldateien:

find a/ |
grep -i 'name'

Aber sie haben alle das Präfix "a /" - was Sinn macht, aber am Ende möchten wir eine Liste von Include-Mustern haben, die für rsync akzeptabel sind, und da das Präfix "a /" für rsync I nicht funktioniert ' ll entfernen Sie es mit Schnitt:

find . |
grep -i 'name' |
cut -d / -f 2-

Es gibt immer noch ein Problem - wir werden immer noch Dateien in Unterverzeichnissen vermissen, da rsync keine Verzeichnisse in der Ausschlussliste durchsucht. Ich werde awk verwenden, um die Unterverzeichnisse aller übereinstimmenden Dateien zur Liste der Include-Muster hinzuzufügen:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}'

Alles, was übrig bleibt, ist, die Liste an rsync zu senden - wir können das Argument --include-from = - verwenden, um eine Liste von Mustern für rsync bei Standardeingabe bereitzustellen. Also insgesamt:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/

Beachten Sie, dass auf das Quellverzeichnis 'a' über zwei verschiedene Pfade verwiesen wird - "a /" und "./a/". Das ist subtil, aber wichtig. Um die Konsistenz zu verbessern, werde ich eine letzte Änderung vornehmen und das Quellverzeichnis immer als "./a/" bezeichnen. Dies bedeutet jedoch, dass der Befehl cut geändert werden muss, da auf der Vorderseite der Ergebnisse von find ein zusätzliches "./" angezeigt wird:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
Woche
quelle
Versucht, es auszuführen, stieß auf Probleme mit dem Befehl cut. Scheint, -tdass dies ein gültiger Schalter ist.
edit: ich meinte -t ​​ist kein gültiger Schalter
Entschuldigung, sollte -d sein. Ich begann mit sed und wechselte dann zu schneiden, weil ich dachte, es sei klarer, aber vergaß, meine Befehle zu bearbeiten: S
Follow-up: Versucht, den Scrip zu bearbeiten, um Argumente zu übernehmen ($ 1 = path_to_search, $ 2 als Muster für egrep), da ich mit Dateiname + Mix von Erweiterungen übereinstimme. Diese Teile funktionieren gut, ich habe die erwartete Liste, aber rsync kann nicht kopiert werden. Es scheint nur mit dem Einzelnamen-Zeichenverzeichnis zu funktionieren, wie in Beispiel (a). Ich vermute, dass der Befehl cut geändert werden muss, um Zeichen basierend auf dem übergeordneten / oder Quellverzeichnis zu schneiden. Ein bisschen verloren, wie man das macht:
user1957413
Ah ja, du hast ganz recht. Es sollte mit einem Verzeichnisnamen beliebiger Länge funktionieren, schlägt jedoch fehl, sobald Sie auf ein Verzeichnis außerhalb des aktuellen Verzeichnisses verweisen (da der Präfixteil eine andere Anzahl von Schrägstrichen enthält). Um dies zu beheben, ist es wahrscheinlich am einfachsten, sed anstelle von cut zu verwenden, z. sed "s#^$1/*##" B .: Buuuut, das auf Pfaden mit einem # unterbrochen wird. Um das zu beheben, müssen wir den Namen des eingehenden Verzeichnisses zitieren: prefix=$(echo "$1" | sed 's#/#\\/#g')und dann sind sed "s/^$prefix\\/*//" die Unterabschnitte des Bash-Zitierens ein Albtraum;)
sqweek
7

Ich würde vorschlagen, die Filteroption von rsync zu verwenden. Geben Sie für Ihr Beispiel einfach Folgendes ein:

rsync -vam -f'+ *[Nn][Aa][Mm][E]*' -f'+ */' -f'- *' a b

Die erste Filterregel teilt rsync mit, welche Muster eingeschlossen werden sollen. Die zweite Regel wird benötigt, um rsync anzuweisen, alle Verzeichnisse auf seiner Durchquerung zu überprüfen. Um zu verhindern, dass leere Verzeichnisse aufgenommen werden, werden sie -moptional explizit ausgeschlossen . Die letzte Filterregel weist rsync an, alle verbleibenden Muster zu entsorgen, die bisher noch nicht übereinstimmen.

Sparkie
quelle
Süss. Das hat auch funktioniert. Ich habe den Ordner a innerhalb von b erhalten, der durch die Verwendung von a / b / als Quelle und Ziel behoben wurde. Vielen Dank!
user1957413
Verwenden Sie -f '+ * [Nn] [Aa] [Mm] [E] **' (zwei Sterne am Ende), um den Inhalt aller Verzeichnisse mit einem bestimmten Namen einzuschließen.
Phobie
2

Wenn Sie ZSH verwenden, können Sie mit dem Flag (#i) die Groß- und Kleinschreibung deaktivieren. Beispiel:

$ touch NAME
$ ls (#i)*name*
NAME

ZSH unterstützt auch Ausschlüsse, die genau wie der reguläre Pfad angegeben werden, aber ein initiales ~ haben

$ touch aa ab ac
$ ls *~*c
aa ab

Sie können Ausschlüsse verketten:

$ ls *~*c~*b
aa

Schließlich können Sie angeben, welche Art von Datei zurückgegeben werden soll (Verzeichnis, Datei usw.). Dies geschieht mit (/) für Verzeichnis und (.) Für Datei.

$ touch file
$ mkdir dir
$ ls *(.)
file

Basierend auf all dem würde ich diesen Befehl wie folgt ausführen:

rsync -avvz *(/) (#i)*name* ./a/ ./b/

(Ich sehe keine Notwendigkeit für einen Ausschluss mit diesen Selektoren)

Matthew Franglen
quelle
1

Die Antwort von @ sqweek oben ist fantastisch, obwohl ich vermute, dass er einen Fehler in seinem awkSkript zum Generieren von übergeordneten Verzeichnissen hat, da er mir z.

$ echo a/b/c/d | awk -F/ '{print; while(/\//) {sub("/[^/]*", ""); print}}'
a/b/c/d
a/c/d
a/d
a

Ich konnte es beheben, indem ich gensubstattdessen Folgendes verwendete:

$ echo a/b/c/d | awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}'
a/b/c/d
a/b/c
a/b
a

Seine vollständige Lösung mit dem awkgeänderten Bit wäre also:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
Ryan Williams
quelle
Vielen Dank. Ich habe meine Antwort mit dem entsprechenden Fix bearbeitet, den regulären Ausdruck am Ende der Zeile ( sub("/[^/]*$")) zu verankern .
Woche
0

Versucht mit einem C # -Skript, da dies die Sprache ist, mit der ich am meisten Erfahrung habe. Ich kann die Liste der Dateien erstellen, die ich einschließen möchte, aber jemand von rsync sagt mir immer noch, dass ich eine Wanderung machen soll. Es erstellt die Ordner, ignoriert jedoch die Dateien. Hier ist was ich habe ..

Zuerst der Inhalt des Verzeichnisses:

~/mono$ ls -l
total 24
drwxr-xr-x 5 me me 4096 Jan 15 00:36 a
drwxr-xr-x 2 me me 4096 Jan 15 00:36 b
drwxr-xr-x 3 me me 4096 Jan 14 00:31 bin
-rw-r--r-- 1 me me 3566 Jan 15 00:31 test.cs
-rwxr-xr-x 1 me me 4096 Jan 15 00:31 test.exe
-rwxr--r-- 1 me me  114 Jan 14 22:40 test.sh

Dann die Ausgabe des C # -Skripts:

~/mono$ mono test.exe

/a/myfile/myfileseries.pdf
/a/myfile2/testfile.pdf

Und die Debug-Ausgabe:

~/mono$ mono test.exe | rsync -avvvz --include='*/' --include-from=- --exclude='*' ./a/ ./b/
[client] add_rule(+ */)
[client] parse_filter_file(-,20,3)
[client] add_rule(+ /a/myfile/myfileseries.pdf)
[client] add_rule(+ /a/myfile2/testfile.pdf)
[client] add_rule(- *)
sending incremental file list
[sender] make_file(.,*,0)
[sender] hiding file 1Name.txt because of pattern *
[sender] showing directory myfile2 because of pattern */
[sender] make_file(myfile2,*,2)
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] showing directory test1 because of pattern */
[sender] make_file(test1,*,2)
[sender] hiding file NaMe.txt because of pattern *
[sender] showing directory myfile because of pattern */
[sender] make_file(myfile,*,2)
send_file_list done
send_files starting
[sender] hiding file myfile/myfileseries.pdf because of pattern *
[sender] hiding file myfile2/testfile.pdf because of pattern *
[sender] hiding file test1/test.txt because of pattern *
user1957413
quelle
0

[EDIT] Dies funktioniert nur lokal. Bei Remote-Pfaden muss zuerst die Verzeichnisstruktur erstellt werden.

Einfacher als die akzeptierte Antwort; Verwenden Sie --file-from, das automatisch übergeordnete Verzeichnisse enthält, und drucken Sie den Dateipfad mit% P.

find /tmp/source -wholename '*[Nn][Aa][Mm][E]*' -printf '%P\n' | rsync -vzrm --exclude='*/' --files-from=- /tmp/source/ /tmp/target/

Sie müssen also nur findund verwenden rsync.

phobisch
quelle