rsync utilisant regex pour inclure uniquement certains fichiers

11

J'essaie d'exécuter rsync pour copier certains fichiers récursivement sur un chemin basé sur leur modèle de nom de fichier, sans tenir compte de la casse . Voici ce que j'ai fait pour exécuter rsync:

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

Rien n'est copié, la sortie de débogage montre:

[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 *

J'ai essayé d'utiliser: --include='*[Nn][Aa][Mm][E]*'et d'autres combinaisons mais ça ne marche toujours pas.

Des idées sur la façon d'utiliser regex pour inclure certains fichiers?

user1957413
la source
4
Pourquoi utilisez-vous le --exclude='*'?
2
il exclut donc tout ce qui ne fait pas partie de l'inclusion.
'masquer le fichier 1Name.txt à cause du modèle ' cela indique: - "est-ce que - la règle d'exclusion doit être dans la commande?" ou Si vous souhaitez exclure certains fichiers, pourquoi un " ".
Akshay Patil

Réponses:

5

rsync ne parle pas regex. Vous pouvez enrôler find et grep, bien que cela devienne un peu mystérieux. Pour rechercher les fichiers cibles:

find a/ |
grep -i 'name'

Mais ils sont tous préfixés par "a /" - ce qui est logique, mais ce que nous voulons nous retrouver c'est une liste de modèles d'inclusion acceptables pour rsync, et comme le préfixe "a /" ne fonctionne pas pour rsync I ' ll le retirer avec coupe:

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

Il y a toujours un problème - nous manquerons toujours des fichiers dans les sous-répertoires, car rsync ne recherche pas les répertoires dans la liste d'exclusion. Je vais utiliser awk pour ajouter les sous-répertoires de tous les fichiers correspondants à la liste des modèles d'inclusion:

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

Il ne reste plus qu'à envoyer la liste à rsync - nous pouvons utiliser l'argument --include-from = - pour fournir une liste de modèles à rsync sur une entrée standard. Donc, tout à fait:

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

Notez que le répertoire source 'a' est référencé via deux chemins différents - "a /" et "./a/". C'est subtil mais important. Pour rendre les choses plus cohérentes, je vais faire un dernier changement, et toujours faire référence au répertoire source comme "./a/". Cependant, cela signifie que la commande cut doit changer car il y aura un "./" supplémentaire sur le devant des résultats de find:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
sqweek
la source
J'ai essayé de l'exécuter, j'ai rencontré des problèmes avec la commande cut. Semble que -tc'est un interrupteur valide.
modifier: je voulais dire -t n'est pas un commutateur valide
désolé, devrait être -d. j'ai commencé à utiliser sed puis j'ai changé pour couper parce que je pensais que c'était plus clair, mais j'ai oublié de modifier mes commandes: S
Suivi: J'ai essayé de modifier le script pour prendre des arguments ($ 1 = path_to_search, $ 2 comme modèle pour egrep) car je fais correspondre le nom de fichier + un mélange d'extensions. Que les pièces fonctionnent bien, j'ai la liste attendue, mais rsync ne parvient pas à copier. Il semble ne fonctionner qu'avec le répertoire de caractères à nom unique, comme dans l'exemple (a), je suppose que la commande cut doit être modifiée pour couper les caractères en fonction du répertoire parent / ou source? Un peu perdu de la façon de le faire:
user1957413
Ah ouais, tu as tout à fait raison. Il devrait fonctionner sur un nom de répertoire de n'importe quelle longueur, mais échouera dès que vous vous référerez à un répertoire en dehors du répertoire actuel (car il y aura un nombre différent de barres obliques dans la partie préfixe). Pour résoudre ce problème, il est probablement plus facile d'utiliser sed au lieu de cut, comme: sed "s#^$1/*##" buuuut qui se cassera sur les chemins qui contiennent un #. Pour résoudre ce problème, nous devons citer le nom du répertoire entrant: prefix=$(echo "$1" | sed 's#/#\\/#g')puis sed "s/^$prefix\\/*//" les sous-qualités de la citation bash sont un peu un cauchemar;)
sqweek
7

Je suggérerais d'utiliser l'option de filtre de rsync. Pour votre exemple, tapez simplement:

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

la première règle de filtrage indique à rsync les modèles à inclure. La deuxième règle est nécessaire pour dire à rsync d'inspecter tous les répertoires sur sa traversée. Pour empêcher l'inclusion de répertoires vides, ils sont exclus explicitement par -moption. La dernière règle de filtrage indique à rsync de supprimer tous les modèles restants qui ne correspondaient pas jusqu'à présent.

sparkie
la source
Sucré. Cela a également fonctionné. J'obtenais le dossier a à l'intérieur de b, qui a été corrigé en utilisant a / b / comme source et destination. Merci!
user1957413
Utilisez -f '+ * [Nn] [Aa] [Mm] [E] **' (deux étoiles à la fin) pour inclure le contenu de tous les répertoires avec un nom spécifique.
phobique
2

Si vous utilisez ZSH, vous pouvez utiliser l'indicateur (#i) pour désactiver la sensibilité à la casse. Exemple:

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

ZSH prend également en charge les exclusions, qui sont spécifiées tout comme le chemin d'accès normal, mais elles ont un ~ initial

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

Vous pouvez chaîner des exclusions:

$ ls *~*c~*b
aa

Enfin, vous pouvez spécifier le type de fichier que vous souhaitez renvoyer (répertoire, fichier, etc.). Cela se fait avec (/) pour le répertoire et (.) Pour le fichier.

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

Sur la base de tout cela, je ferais cette commande comme:

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

(Je ne vois pas la nécessité d'une exclusion avec ces sélecteurs)

Matthew Franglen
la source
1

La réponse de @ sqweek ci-dessus est impressionnante, même si je soupçonne qu'il a un bug dans son awkscript pour générer des répertoires parents, car cela me donne par exemple:

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

J'ai pu le réparer en utilisant à la gensubplace:

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

Donc, sa solution complète, avec le awkbit changé, serait:

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
la source
Merci. Modifié ma réponse avec le correctif équivalent d'ancrage de l'expression régulière à la fin de la ligne ( sub("/[^/]*$")).
sqweek
0

J'ai essayé avec un script C # car c'est le langage avec lequel j'ai le plus d'expérience. Je peux créer la liste des fichiers que je veux inclure, mais quelqu'un rsync me dit toujours de faire une randonnée. Il crée les dossiers, mais il ignore les fichiers. Voici ce que j'ai obtenu ..

D'abord le contenu du répertoire:

~/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

Ensuite, la sortie du script C #:

~/mono$ mono test.exe

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

Et la sortie de débogage:

~/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
la source
0

[EDIT] Cela ne fonctionne que localement. Pour les chemins distants, la structure de répertoires doit d'abord être créée.

Plus simple que la réponse acceptée; Utilisez --file-from, qui inclut automatiquement les répertoires parents et affichez le chemin du fichier avec% P

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

Vous n'avez donc qu'à utiliser findet rsync.

phobique
la source