总结:我想要将rsync
一个目录返回到它自己的后代,然后rsync
将所述后代返回到原始目录 - 包括两个方向的删除和排除。
在您提出显而易见的问题“您为什么要这样做?”或指出另一种方法有多好之前,这是一个业务需求。这不是我的选择,而且我意识到了风险,所以请放纵我。我不打算进一步证明这种方法的合理性。
答案1
解决方案分为三个部分。按顺序...
- 排除后代目录路径相对于祖先例如,
child
从parent/child
- 使用
--delete-after
标志代替--delete
- 使所有排除路径相对于源,例如,
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 的解决方案优雅,但可能更为强大。