как перезаписать целевые файлы с помощью mv?

<p>у меня есть тонна файлов и каталогов в подкаталоге, который я хочу переместить в родительский каталог. в целевом каталоге уже есть несколько файлов и каталогов, которые необходимо перезаписать. файлы, которые присутствуют только в цели, должны быть оставлены нетронутыми. могу ли я заставить <code>mv</code> сделать это? это (<code>mv * ..</code>) жалуется</p><pre><code>mv: cannot move `xyz' to `../xyz': directory not empty </code></pre><p>что мне не хватает?</p>

<p>вам нужно будет скопировать их в место назначения, а затем удалить источник, используя команды <code>cp -r * ..</code> с последующим <code>rm -rf *</code>.</p><p>я не думаю, что вы можете "объединить" каталоги, используя <code>mv</code>.</p>

<p><code>rsync</code>, вероятно, будет лучшим вариантом здесь. это так же просто, как <code>rsync -a subdir/ ./</code>.</p><p>мое тестовое дерево в формате <code>filename</code>: <code>contents</code>:</p><pre><code>./file1:root ./file2:root ./dir/file3:dir ./dir/file4:dir ./subdir/dir/file3:subdir ./subdir/file1:subdir </code></pre><p>запуск <code>rsync</code>:</p><pre><code>$ rsync -a -v subdir/ ./ sending incremental file list ./ file1 dir/ dir/file3 дает:./file1:subdir ./file2:root ./dir/file3:subdir ./dir/file4:dir ./subdir/dir/file3:subdir ./subdir/file1:subdir и затем, чтобы эмулировать mv, вы, вероятно, захотите удалить исходный каталог:$ rm -r subdir/ </code></pre><p>предоставление:</p><pre><code>./file1:subdir ./file2:parent ./dir/file3:subdir ./dir/file4:dir </code></pre><p>если это не так, можете ли вы привести аналогичный пример (например, используя мое тестовое дерево в верхней части этого ответа) с желаемым результатом?</p>

<p><code>rsync</code> может удалить источник после копирования с помощью <code>--remove-source-files</code> параметр. это должен быть удобный способ сделать то, что вы хотите.</p><p>от <code>rsync man page</code>:</p><pre><code> --remove-source-files sender removes synchronized files (non-dir) </code></pre>

<p>вы можете сделать это с помощью <code>cp</code> и <code>rm</code>, но не копируя огромное количество данных, которые вы (предположительно) пытаетесь избежать передачи. @mattdm упомянул об этом в <a href="https://unix.stackexchange.com/q/9899/13308#comment12749\_9902">его комментарий</a> и <a href="https://unix.stackexchange.com/a/172402/13308">ответ на другой вопрос</a> более полно обсуждает различные варианты.</p><pre><code>cp -rlf source destination rm -r source </code></pre><p>по сути, опция <code>-l</code> для команды <code>cp</code> создает жесткие ссылки на файлы, а не копирует их данные в новые файлы.</p>

<p>вот скрипт, который перемещает файлы из-под <code>/path/to/source/root</code> к соответствующему пути под <code>/path/to/destination/root</code>.</p><ul> <li>если каталог существует как в источнике, так и в месте назначения, содержимое рекурсивно перемещается и объединяется.</li> <li>если файл или каталог существует в источнике, но не в месте назначения, он перемещается.</li> <li>любой файл или каталог, который уже существует в месте назначения, остается позади. (в частности, объединенные каталоги остаются в источнике. это не легко исправить.)</li> </ul><p>осторожно, непроверенный код.</p><pre><code>export dest='/path/to/destination/root' cd /path/to/source/root find . -type d \( -exec sh -c '[ -d "$dest/$0" ]' {} \; -o \ -exec sh -c 'mv "$0" "$dest/$0"' {} ; -prune )  -o -exec sh -c ' if ! [ -e "$dest/$0" ]; then mv -f "$0" "$dest/$0"; fi ' {} \; </code></pre>

<p>эта тема существует уже много лет и по-прежнему занимает первое место в google, поэтому я хотел добавить еще один метод. как я обычно это делаю: упаковываю содержимое subdir в tarball, перемещаем tarball в родительский каталог и затем извлекаю его с поведением по умолчанию --overwrite. это именно то, что вы ищете. после этого вы можете удалить свой subdir.</p><pre><code>cd xyz tar -cvzpf tmp.tar.gz * mv tmp.tar.gz ../tmp.tar.gz cd .. tar -xvzpf tmp.tar.gz rm -rf xyz rm -f tmp.tar.gz </code></pre>

<p>если у вас достаточно памяти, вы можете сделать это следующим образом:</p><pre><code>mv -bfv directory\_1/* directory\_2/ # all duplicate source files/directories # will have ~ appended to them find -name "*~" -delete # will recursively find and delete all files # with ~ on the end </code></pre><p>убедитесь, что нет важных файлов с ~ в конце, но если они есть, вы можете добавить <code>--suffix=whateveryouwant</code> вместо значения по умолчанию.</p>