Bash

使用 Rsync 檢測目錄移動

  • January 24, 2021

我目前用於將一個 HDD 備份到另一個(本地,而不是遠端)的命令是

rsync --info=PROGRESS2,BACKUP,DEL -ab --human-readable --inplace --delete-after --debug=NONE --log-file=/media/blueray/WDPurple/rsync.log --backup-dir=red_rsync_bak.$(date +"%d-%m-%y_%I-%M-%S%P") --log-file-format='%t %f %o %M' --exclude='lost+found' --exclude='.Trash-1000' /media/blueray/WDRed /media/blueray/WDPurple

如果我使用--delete-afterrsync 將移動的目錄視為已刪除和創建的目錄。

結果,當我在源中移動目錄時,它會從目標中刪除這些目錄,然後從源中複製它們。通常需要很長時間,因為我有時會在原始碼中移動大目錄。

我發現這個問題的解決方案很少。

  1. 更新檔 rsync
  2. 沒有更新檔
  3. 使用 BorgBackup 或 bup
  4. 利用--fuzzy --delay-updates --delete-delay

然而,每個人都有自己的問題。

該更新檔是很久以前創建的,我不確定它是否會與現代 rsync 有問題。此外,維護更新檔對我來說很困難。

選項二在我的硬碟中造成混亂。此外,我使用了更多rsync選項,不確定它是否安全。

就選項 3 而言,我投入了大量時間,rsync現在不想轉向新工具。此外,這些工具也有其自身的問題。

關於選項 4,使用--fuzzy --delay-updates --delete-delay/test/10GBfile 重命名為 /test/otherdir/10GBfile_newname 仍會重新發送數據,因為它不在同一目錄中。它還有很多問題。前任。--delay-updates與 衝突--inplace

所以,我正在尋找的解決方案是**使用--itemize-changes--dry-run獲取移動的目錄列表,然後首先mv在目標中執行(如果它有一個提示,比如 x 將被移動到目的地的 a/x,y 將是移動到 b/y in destination, c/z 將移動到 z in destination. 你要繼續嗎?) 然後執行我rsync在頂部提到的命令。**我準備考慮與類似目錄具有相同名稱和大小的目錄。

假設目錄樹如下所示:

.
├── dest
│   ├── test
│   │   └── empty-asciidoc-document.adoc
│   ├── test2
│   │   └── empty-asciidoc-document.adoc
│   └── test3
│       └── empty-asciidoc-document.adoc
├── src
│   ├── grandpartest1
│   │   └── partest
│   │       └── test1
│   │           └── empty-asciidoc-document.adoc
│   ├── grandpartest2
│   │   └── partest2
│   │       └── test2
│   │           └── empty-asciidoc-document.adoc
│   └── grandpartest3
│       └── partest3
│           └── test3
│               └── empty-asciidoc-document.adoc

我注意到,如果我移動目錄,--itemize-changes輸出看起來像:

% rsync --dry-run -ai --inplace --delete-after /home/blueray/Downloads/src/ /home/blueray/Downloads/dest/
.d..t...... ./
cd+++++++++ grandpartest/
cd+++++++++ grandpartest/partest/
cd+++++++++ grandpartest/partest/test/
>f+++++++++ grandpartest/partest/test/empty-asciidoc-document.adoc
cd+++++++++ grandpartest2/
cd+++++++++ grandpartest2/partest2/
cd+++++++++ grandpartest2/partest2/test2/
>f+++++++++ grandpartest2/partest2/test2/empty-asciidoc-document.adoc
cd+++++++++ grandpartest3/
cd+++++++++ grandpartest3/partest3/
cd+++++++++ grandpartest3/partest3/test3/
>f+++++++++ grandpartest3/partest3/test3/empty-asciidoc-document.adoc
*deleting   test3/empty-asciidoc-document.adoc
*deleting   test3/
*deleting   test2/empty-asciidoc-document.adoc
*deleting   test2/
*deleting   test/empty-asciidoc-document.adoc
*deleting   test/

我們可以使用以下方法獲取已刪除的目錄:

% echo "$dryrunoutput" | grep "*deleting.*/$" | awk '{print $2}' | while read spo; do echo ${spo%?}; done
test3
test2
test

添加目錄使用:

% echo "$dryrunoutput" | grep "cd++.*/$" | awk '{print $2}' | while read spo; do echo ${spo%?}; done | while read spo; do echo ${spo##*/}; done
grandpartest
partest
test
grandpartest2
partest2
test2
grandpartest3
partest3
test3

使用以下命令添加和刪除的目錄:

$ sort  <(echo "$deletedirectories") <(echo "$addeddirectoriesvalue") | uniq -d
test
test2
test3

以字節為單位的目錄大小,比較兩者是相同的目錄(或多或少,這對我有用),使用:

% /usr/bin/du -sb "/home/blueray/Documents/src/test2/test" | grep -oh "^\S*"
4096
% /usr/bin/du -sb "/home/blueray/Documents/dest/test" | grep -oh "^\S*"
4096

到目前為止我想出的腳本是:

#!/bin/bash

source="/media/blueray/WDRed/_working/_scripts/_rsync-test/src/"
destination="/media/blueray/WDRed/_working/_scripts/_rsync-test/dest/"
dryrunoutput=$(rsync --dry-run -ai --inplace --delete-after $source $destination)
deletedirectories=$( echo "$dryrunoutput" | grep "*deleting.*/$" | awk '{print $2}' | while read spo; do echo ${spo%?}; done )
addeddirectorieskey=$( echo "$dryrunoutput" | grep "cd++.*/$" | awk '{print $2}' | while read spo; do echo ${spo%?}; done )
addeddirectoriesvalue=$( echo "$dryrunoutput" | grep "cd++.*/$" | awk '{print $2}' | while read spo; do echo ${spo%?}; done | while read spo; do echo ${spo##*/}; done )

intersection=$( sort  <(echo "$deletedirectories") <(echo "$addeddirectoriesvalue") | uniq -d )

sourcesize=$(/usr/bin/du -sb "${source}test2/test" | grep -oh "^\S*")

destsize=$(/usr/bin/du -sb "${destination}test" | grep -oh "^\S*")

if [[ "$destsize" == "$sourcesize" ]]
then
 mv "${destination}test/" "$destination$addeddirectories"
fi

如果您注意到mv "${destination}test/" "$destination$addeddirectories",這裡的部分路徑是硬編碼的。它還有其他問題。它僅適用於單個目錄和類似的東西。

PS我知道相似的名稱和大小並不意味著它們相同,但在我的情況下它會起作用。我的目錄是主要問題,文件不是。所以,我並不擔心文件移動檢測。我只對目錄移動檢測感興趣。

您可以將此作為備份的基礎。它要求源文件系統和目標文件系統可以處理硬連結文件,並且您不介意目標文件在執行之間保持硬連結到工作目錄中。寫出文件的 inode 和相對路徑find的選項依賴於 GNU 的版本。-printf

#!/bin/bash
# Usage: [<rsync_args...>] <src> <dst> 
#
args=("$@")
src="${args[-2]}"          # '.'
dst="${args[-1]}"          # eg 'remote:/tmp/dest'
unset args[-1] args[-1]    # Yes really


# Create the working set space
#
temp=".inodes"
mkdir -p "$src/$temp"


# Build the set of files indexed by inode
#
echo Create inodes >&2
find "$src" -path "$src/$temp" -prune -o -type f -printf "%i\t%P\0" |
   while IFS= read -d '' -r line
   do
       inode="${line%%$'\t'*}" file="${line#*$'\t'}"
       ln -f "$src/$file" "$src/$temp/$inode"
   done


# Copy the index and then the full tree
#
echo Copy inodes >&2
rsync -avPR "${args[@]}" "$src/./$temp/" "$dst/"

echo Copy structure >&2
rsync -avHPR --delete-after "${args[@]}" "$src/./$temp/" "$src/./" "$dst/"


# Remove the working set on the source (not essential but you may prefer it)
#
echo Tidyup >&2
rm -rf "$src/$temp"

如果您呼叫它dsync並將其放入您的路徑中,您可以像這樣使用它

dsync /media/blueray/WDRed /media/blueray/WDPurple

或可能

dsync --info=PROGRESS2,BACKUP,DEL --backup --human-readable --inplace --delete-after --log-file=/media/blueray/WDPurple/rsync.log --backup-dir=red_rsync_bak.$(date +"%d-%m-%y_%I-%M-%S%P") --log-file-format='%t %f %o %M' --exclude='lost+found' --exclude='.Trash-1000' /media/blueray/WDRed /media/blueray/WDPurple

引用自:https://unix.stackexchange.com/questions/628201