遞歸移動(mv -rn
,如cp -rn
),只會移動不存在的文件的移動
語境
我有使用者上傳的內容需要備份。內容超過 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/
為了完整起見,這是一個基於源
find
和mv
目標位於同一文件系統上的情況的解決方案(在這種情況下,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 {} +