rsync 一个目录及其自己的后代

rsync 一个目录及其自己的后代

总结:我想要将rsync一个目录返回到它自己的后代,然后rsync将所述后代返回到原始目录 - 包括两个方向的删除和排除。

在您提出显而易见的问题“您为什么要这样做?”或指出另一种方法有多好之前,这是一个业务需求。这不是我的选择,而且我意识到了风险,所以请放纵我。我不打算进一步证明这种方法的合理性。

答案1

解决方案分为三个部分。按顺序...

  1. 排除后代目录路径相对于祖先例如,childparent/child
  2. 使用--delete-after标志代替 --delete
  3. 使所有排除路径相对于源,例如,exclude.txt而不是/var/www/parent/exclude.txt

这是一个测试脚本(为了得到更好的输出有一些特殊的格式):

#!/usr/bin/env bash

clear

printf "\n\e[33mrsync version:\n"
printf "%s--------------\e[39m\n"

rsync --version | head -n 1
# rsync  version 2.6.9  protocol version 29

rm -r parent

mkdir -p parent

touch parent/original.txt \
      parent/delete-from-child.txt \
      parent/exclude-from-child.txt

printf "\n\e[33mOriginal state:\n"
printf "%s---------------\e[39m\n"

tree -a --noreport -- parent

printf "\n\e[33mrsync to child:\n"
printf "%s---------------\e[39m\n"

printf "rsync --archive \\n      --delete-after \\n      --exclude=exclude-from-child.txt \\n      "$PWD"/parent/ \\n      "$PWD"/parent/child\n\n"
rsync --archive \
      --delete-after \
      --exclude=exclude-from-child.txt \
      "$PWD"/parent/ \
      "$PWD"/parent/child

tree -a --noreport -- parent; echo

printf "\e[33mMake changes:\n"
printf "%s---------------\e[39m\n"

rm parent/child/delete-from-child.txt
printf "rm parent/child/delete-from-child.txt\n"
touch parent/child/create-in-child.txt
printf "touch parent/child/create-in-child.txt\n"
touch parent/child/exclude-from-parent.txt
printf "touch parent/child/exclude-from-parent.txt\n\n"

printf "\e[33mrsync back to parent:\n"
printf "%s---------------------\e[39m\n"

printf "rsync --archive \\n      --delete-after \\n      --exclude=child \\n      --exclude=exclude-from-child.txt \\n      --exclude=exclude-from-parent.txt \\n      "$PWD"/parent/child/ \\n      "$PWD"/parent\n\n"
rsync --archive \
      --delete-after \
      --exclude=child \
      --exclude=exclude-from-child.txt \
      --exclude=exclude-from-parent.txt \
      "$PWD"/parent/child/ \
      "$PWD"/parent

tree -a --noreport -- parent; echo
# Or `find parent -type f; echo`, if you don't have `tree`

printf "%s\n\e[33mFinal comparison:\n"
printf "%s-----------------\e[39m\n"

diff --exclude=child parent parent/child; echo

输出:

rsync version:
--------------
rsync  version 2.6.9  protocol version 29

Original state:
---------------
parent
├── delete-from-child.txt
├── exclude-from-child.txt
└── original.txt

rsync to child:
---------------
rsync --archive
      --delete-after
      --exclude=exclude-from-child.txt
      /var/www/parent/
      /var/www/parent/child

parent
├── child
│   ├── delete-from-child.txt
│   └── original.txt
├── delete-from-child.txt
├── exclude-from-child.txt
└── original.txt

Make changes:
---------------
rm parent/child/delete-from-child.txt
touch parent/child/create-in-child.txt
touch parent/child/exclude-from-parent.txt

rsync back to parent:
---------------------
rsync --archive
      --delete-after
      --exclude=child
      --exclude=exclude-from-child.txt
      --exclude=exclude-from-parent.txt
      /var/www/parent/child/
      /var/www/parent

parent
├── child
│   ├── create-in-child.txt
│   ├── exclude-from-parent.txt
│   └── original.txt
├── create-in-child.txt
├── exclude-from-child.txt
└── original.txt


Final comparison:
-----------------
Only in parent: exclude-from-child.txt
Only in parent/child: exclude-from-parent.txt

答案2

另一种方法是设置绑定挂载,从而使两棵树看起来独立,然后正常进行 rsync:

mkdir /mnt/parent
mkdir /mnt/child
mkdir /mnt/empty
mount --bind /path/to/parent/child /mnt/child
mount --bind /path/to/parent /mnt/parent
mount --bind /mnt/empty /mnt/parent/child

现在,当你这样做

rsync [args] /mnt/parent/. /mnt/child/.

空目录/mnt/empty将掩盖真实/path/to/parent/child目录;/mnt/parent/child将显示为空并避免无限递归。

当你这样做

rsync [args] /mnt/child/. /mnt/parent/.

情况会类似;/mnt/child/child/会是空的(因为你首先不会在那里复制任何东西),并且/mnt/parent/child/也会显示为空,所以不存在从那里删除任何东西的风险。

这可能不如@TravisCarden 的解决方案优雅,但可能更为强大。

相关内容