Files

遞歸移動(mv -rn,如cp -rn),只會移動不存在的文件的移動

  • August 20, 2021

語境

我有使用者上傳的內容需要備份。內容超過 3 個獨立的伺服器/var/www/domain/media/(在每個伺服器上都在同一個地方)。備份是一個 NFS 掛載的 RAID,位於/var/www/domain/bak/.

media/由不同的使用者擁有bak/,基本上 webapp 可以寫入media/但只能讀取bak/(使用者只能刪除他們的上傳,直到它在格林威治標準時間 00:00 備份)。

這導致了兩個問題:使用者可以強制使用相同的文件名覆蓋備份中的文件,並且其中的文件media/最終可以在兩個不同的伺服器上(如果使用者上傳兩次並且它由不同的伺服器)。

所有這些都在 4 CenOS 7 上執行(網路 X 3 + 備份 X 1)。“網路”伺服器的磁碟空間有限,需要將內容移動到備份伺服器以防止它們填滿磁碟。

沒有競爭條件,所以這是我們不需要關心的事情。備份是從單個備份機器上完成的,通過ssh在其他三台機器上按順序執行命令。


目前解決方案

要備份的文件的“移動”是在清除重複項後完成的:

find /var/www/domain/media/ -type f | > media
find /var/www/domain/bak/ -type f | awk '{a=gensub("bak","media",1); print a}' > bak
cat bak media | sort | uniq -d > dupes
cat dupes | xargs rm
cp -r /var/www/domain/media/* /var/www/domain/bak/
rm -rf /var/www/domain/media/*

使用的問題mv/var/www/domain/media/每個使用者都有子目錄。例如:

media/user13/myvideo.webm
media/user13/walk-in-the-park.webm
media/user16/cat-video.webm
media/user17/presentation-may-2016.webm

bak/user13/mountai-trip.webm
bak/user13/walk-in-the-park.webm
bak/user14/reax-the-dog.webm

user16該命令必須為and創建目錄user17,同時它必須避免覆蓋bak/user13/walk-in-the-park.webm


目前解決方案的問題

我想保留重複項media/而不是刪除它們。將它們複製到另一個地方會遇到同樣的問題,因為新文件會在白天出現,我需要將復製品與他們的副本同步。

如何移動所有media/不在其中的文件,bak/同時保持目錄結構而不刪除已經存在的文件。

換句話說,我正在尋找可以執行的動作:

source      | destination         | action
----------- | ------------------- | ----------------------------------
file exists | file does not exist | move (`mv`), source -> destination
file exists | file exists         | do nothing, both files stay as they are
no file     | file exists         | do nothing (will not trigger)
no file     | file does not exist | do nothing (well, there's nothing to do something with!)

嘗試更優雅的解決方案

我相信rsync應該能夠做到這一點。我知道,--remove-source-files但我找不到檢查時間戳、校驗和、文件大小等所有內容的方法。

我將校驗和作為一個完全獨立的過程進行保存和檢查。

我只關心文件名。我知道這可能會導致文件損壞,但恐怕在普通磁碟上而不是在 RAID 伺服器上獲取損壞的文件要容易得多。

歡迎非rsync解決方案。我想編寫一個shell-script 來執行移動(從目前解決方案部分擴展腳本)。然而,一旦我意識到它是多麼容易出錯,我就放棄了。

我也試過:

tar -cf /var/www/domain/media | (cd /var/www/domain/bar; tar -kxf -)

但它對於媒體文件(可能相當大)來說太慢了,並且將所有文件保留在media/(磁碟空間有限)。

如果文件已存在於目標樹中(不管任何元數據),則不執行任何操作,請將選項傳遞--ignore-existing給 rsync。

rsync -a --remove-source-files --ignore-existing /var/www/domain/media/ /var/www/domain/bak/

為了完整起見,這是一個基於源findmv目標位於同一文件系統上的情況的解決方案(在這種情況下,rsync這不是一個好的解決方案,因為它複製然後刪除文件,而不是簡單地將它們移動到目標目錄) .

cd /var/www/domain/media
find -type f -exec '
 for x; do
   if ! [ -e "/var/www/domain/bak/$x" ]; then
     mkdir -p "/var/www/domain/bak/${x%/*}" &&
     mv -- "$x" "/var/www/domain/bak/$x"
   fi
 done
' sh {} +

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