mv с функциональностью rsync

Я пытаюсь объединить два дерева каталогов, которые имеют много общих элементов, но также имеют элементы, которые присутствуют только в одном из двух деревьев. Основная проблема, которую я испытываю, заключается в том, что когда mv сталкивается с двумя подкаталогами с одинаковыми относительными путями, он либо сохраняет источник (с -f ), либо пункт назначения (с -n ), но я не могу заставить его принять объединение обоих подкаталоги. Я мог бы, конечно, использовать rsync с --remove-source-files но это фактически скопирует данные, а затем удалит старые файлы, а не истинный ход. Два дерева каталогов содержат несколько сотен ГБ данных и оба находятся на одном разделе, поэтому я хотел бы сделать настоящий ход, если это возможно во времени.

Прямо сейчас у меня есть find * -type file -exec mv -n {} /destination/{} но это только перемещает файлы из источника в пункт назначения, когда целевой каталог уже существует. Я могу предикатировать эту команду с чем-то вроде mv -n * /destination/ но это приводит только к каталогам верхнего уровня. Есть ли способ сделать это в одной строке? Я всегда мог написать сценарий, чтобы проверить, существует ли каталог перед копированием файла, но это похоже на такую ​​базовую задачу, кажется, что должен быть более простой способ.

Предупреждение. Этот ответ не будет работать в файловых системах, которые не поддерживают жесткие ссылки (например, FAT).

Другой вариант (который может быть более переносимым)

 cd source_directory
 найти .  -тип f -print0 |  cpio - pass-through --null --link --make-каталоги dest_dir

cpio (copy in & out) – динозавр, который предшествует tar . Как и tar , он может создавать или извлекать из архивов. В отличие от tar (исправьте меня, если я ошибаюсь), он может скопировать деревья каталогов одной командой. (Я думаю, вы могли бы сделать это с помощью tar -cf - source option(s) and arguments(s) | tar -xf - destination option(s) and arguments(s) .) Вот что --pass-through . --null означает «ожидать, что имена файлов будут разделены нулями»; т.е. считывать вывод из find … -print0 . --link означает «связывать файлы из исходного каталога в целевой каталог, если это возможно». --make-directories требуют объяснений.

Это может быть сокращено cpio –p0ld dest_dir . Добавьте --verbose или -v если хотите.

Затем, после этого заканчивается,

  • Проверьте наличие столкновений и обработайте их соответствующим образом.
  • Убедитесь, что ваш целевой каталог заполнен жесткими ссылками.
  • Удалите исходный каталог.

Вы можете использовать prename чтобы получить то, что хотите. В некоторых дистрибутивах (например, Debian / Ubuntu) это должно быть установлено по умолчанию и псевдонимом для rename . Другие дистрибутивы могут использовать другое rename . Вы можете перейти в каталог выше исходного каталога и сделать следующее:

 find source -exec prename 's:^source:/path/to/dest:' {} + 

Это позволит отказаться от перемещения файлов, которые уже существуют в дереве назначения, и оставить пустые каталоги в том случае, когда имена каталогов перекрываются, поэтому после их удаления вы удалите их. Вы можете добавить параметр -f в prename чтобы перезаписать существующие файлы.

Пример:

 $ mkdir -p dir1/{common,sub1} dir2/{common,sub2} $ touch dir1/sub1/file dir2/sub2/file dir1/common/common dir2/common/common dir1/common/diff1 dir2/common/diff2 $ tree dir* dir1 ├── common │  ├── common │  └── diff1 └── sub1 └── file dir2 ├── common │  ├── common │  └── diff2 └── sub2 └── file 4 directories, 6 files $ find dir2 -depth -exec rename 's/^dir2/dir1/' {} + Can't rename dir2/sub2/file dir1/sub2/file: No such file or directory dir2/common/common not renamed: dir1/common/common already exists dir2/common not renamed: dir1/common already exists dir2 not renamed: dir1 already exists $ tree dir* dir1 ├── common │  ├── common │  ├── diff1 │  └── diff2 ├── sub1 │  └── file └── sub2 └── file dir2 └── common └── common 4 directories, 6 files 

Обновить:

Чтобы предоставить источник для prename , он обычно поставляется в комплекте с perl (следовательно, «p»). В Debian / Ubuntu это часть пакета perl . Если вы хотите получить его отдельно, один из ответчиков на этот вопрос – Get Perl rename, вместо встроенного переименования , сделал для него отдельный репозиторий – https://github.com/subogero/rename