Как использовать Rsync для копирования только определенных подкаталогов (одинаковые имена в нескольких каталогах)

У меня есть такая структура каталогов на сервере 1:

  • data
    • company1
      • unique_folder1
      • другая_папка
      • ...
    • company2
      • unique_folder1
      • ...
    • ...

И я хочу дублировать эту структуру папок на сервере 2, но копировать только каталоги/подкаталоги из unique_folder1. т.е. как результат должен быть:

  • data
    • company1
      • unique_folder1
    • company2
      • unique_folder1
    • ...

Я знаю, что rsync очень хорош для этого. Я безуспешно пробовал варианты «включить/исключить».

Например. Я пытался:

rsync -avzn --list-only --include '*/unique_folder1/**' --exclude '*' -e ssh [email protected]:/path/to/old/data/ /path/to/new/data/

Но в результате я не вижу никаких файлов/каталогов:

receiving file list ... done
sent 43 bytes  received 21 bytes  42.67 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

Что случилось? Идеи?


Дополнительная информация: у меня есть доступ sudo к обоим серверам. У меня есть одна идея - использовать команду find и cpio вместе, чтобы скопировать в новый каталог нужный мне контент, а после этого использовать Rsync. Но это очень медленно, файлов много и т.д.


person Andron    schedule 28.03.2013    source источник


Ответы (4)


Я нашел причину. Что касается меня - не было ясно, что Rsync работает таким образом.
Поэтому правильная команда (только для каталога company1) должна быть:

rsync -avzn --list-only --include 'company1/' --include 'company1/unique_folder1/***' --exclude '*' -e ssh [email protected]:/path/to/old/data/ /path/to/new/data

т.е. нам нужно включить каждый родительский каталог company. И конечно мы не можем прописать вручную все эти company каталогов в командной строке, поэтому сохраняем список в файл и пользуемся им.


Последнее, что нам нужно сделать:

1. Создайте включаемый файл на сервере 1, поэтому его содержимое будет (я использовал ls и awk):

+ company1/  
+ company1/unique_folder1/***  
...  
+ companyN/  
+ companyN/unique_folder1/***  

2. Скопируйте include.txt на сервер 2 и используйте такую ​​команду:

rsync -avzn                                        \
      --list-only                                  \
      --include-from '/path/to/new/include.txt'    \
      --exclude '*'                                \
      -e ssh [email protected]:/path/to/old/data/    \
      /path/to/new/data
person Andron    schedule 29.03.2013
comment
Привет, Андрон, есть ли причина, по которой ты использовал тройные звездочки? Я экспериментировал и с двумя, и с тремя, и я не могу сказать разницы. И я использую эту технику для резервного копирования некоторых файлов прямо сейчас, спасибо за публикацию. - person Chad von Nau; 11.07.2013
comment
Ничего себе, я разобрался. Я делал folder** вместо folder/***. Вам нужна третья звездочка, когда вы используете косую черту после имени каталога. Метод с двумя звездочками и без косой черты также работает, но менее точен, потому что он также будет соответствовать одноранговым папкам с тем же базовым именем. - person Chad von Nau; 11.07.2013
comment
@ChadvonNau хм, не знаю, почему я использовал ***. В документах RSync я вижу use '**' to match anything, including slashes. Так что, возможно, 2 звездочки достаточно. Но я думаю, что 3 лучше :) - person Andron; 11.07.2013
comment
Также рассмотрите это unix.stackexchange.com/a/42691/37431, если вы хотите исключить самый верхний каталог - person rofrol; 11.10.2013
comment
-n и --list-only здесь для тестирования? Я новичок в rsync и понятия не имел, почему команда ничего не делает, кроме вывода списка. - person Kagami Sascha Rosylight; 25.08.2018
comment
Касательно трех звездочек; Страница руководства Rsync определяет... trailing "dir_name/***" will match both the directory (as if "dir_name/" had been specified) and everything in the directory (as if "dir_name/**" had been specified). This behavior was added in version 2.6.7 - person Dogsbody; 21.01.2019

Если первый соответствующий шаблон исключает каталог, то все его потомки никогда не будут пройдены. Если вы хотите включить глубокий каталог, например. company*/unique_folder1/**, но исключите все остальное *, вам нужно указать rsync, чтобы он также включал всех своих предков:

rsync -r -v --dry-run                       \
    --include='/'                           \
    --include='/company*/'                  \
    --include='/company*/unique_folder1/'   \
    --include='/company*/unique_folder1/**' \
    --exclude='*'

Вы можете использовать расширение скобок bash, чтобы сэкономить время на вводе. После раскрытия фигурной скобки следующая команда точно такая же, как и предыдущая:

rsync -r -v --dry-run --include=/{,'company*/'{,unique_folder1/{,'**'}}} --exclude='*'
person yonran    schedule 06.11.2014
comment
Спасибо @yonran, как вы можете видеть ниже, «список включения» слишком велик. Вот почему список был помещен в файл (см. принятый ответ ниже). И спасибо за расширение скобок bash — нужно попробовать. - person Andron; 07.11.2014
comment
Этот ответ действителен, хотя, если мы используем функции bash, то мы входим в серую зону :) В этом случае стоит отметить, что подойдет простой shopt -s globstar; rsync -avn --relative /sourcepath/./**/a destpath. - person Marcus; 13.02.2021

Альтернативой Andron's Answer, которая во многих случаях проще для понимания и реализации, является использование параметра --files-from=FILE. Для текущей проблемы,

rsync -arv --files-from='list.txt' old_path/data new_path/data

Где list.txt просто

company1/unique_folder1/
company2/unique_folder1/
...

Обратите внимание, что флаг -r должен быть включен явно, так как --files-from отключает такое поведение флага -a. Мне также кажется, что построение пути отличается от других команд rsync тем, что company1/unique_folder1/ совпадает, а /data/company1/unique_folder1/ нет.

person Pippip19    schedule 26.12.2020
comment
Этот метод был намного проще для меня, так как он позволял использовать find для создания списка каталогов для включения. - person Sam R; 20.05.2021

Например, если вы хотите синхронизировать только target/classes/ и target/lib/ с удаленной системой, выполните

rsync -vaH --delete --delete-excluded --include='classes/***' --include='lib/***' \
      --exclude='*' target/ user@host:/deploy/path/

Что важно посмотреть:

  • Не забудьте "/" в конце путей, иначе вы получите копию в подкаталог.
  • Порядок --include, --exclude отсчетов.
  • В отличие от других ответов, начиная с «/», параметр include/exclude не нужен, они автоматически добавляются в исходный каталог (в примере target/).
  • Чтобы проверить, что именно произойдет, мы можем использовать флаги --dry-run, как говорят другие ответы.
  • --delete-excluded удалит все содержимое в целевом каталоге, кроме специально включенных подкаталогов. Его нужно использовать с умом! По этой причине --delete недостаточно, он не удаляет исключенные файлы на удаленной стороне по умолчанию (да, все остальные), он должен указываться рядом с обычным --delete, опять же.
person peterh    schedule 12.04.2019
comment
Спасибо. Это хорошая идея. Но в моем случае вы можете видеть, что у меня было одно и то же имя подкаталога в X разных каталогах. Поэтому я не уверен, что это возможно сделать с вашей идеей. - person Andron; 13.04.2019
comment
@ Андрон Это правда. Я думаю, что список параметров --include должен быть изменен, может быть, на --include='***/dirName/' или что-то подобное. Я не проверял это, но мой пример взят из реального, проверенного, работающего сценария развертывания. - person peterh; 14.04.2019
comment
Этот ответ на самом деле не отвечает на вопрос. Предлагаемый --include='***/dirName/' не работает должным образом. - person Marcus; 13.02.2021
comment
@Marcus Маркус Я использовал его в начале 2019 года, использую и сейчас, и он работает по назначению. Не могли бы вы объяснить, что именно у вас не работает? - person peterh; 13.02.2021
comment
Образец следует; ничего не синхронизируется: cd /tmp; mkdir -p data/company{1,2}/{unique_folder1,other_folder}; touch data/company{1,2}/{unique_folder1,other_folder}/testfile; tree data; rsync -vaH --include='***/unique_folder1/' --exclude='*' data/ dest - person Marcus; 13.02.2021