title: 【Shell Script】使用 crontab + script 定期備份並保留30天
date: 2019-06-14
is_modified: false
disqus: cynthiahackmd
categories:
這是 CodiMD 安裝的後續…,雖然跟 CodiMD 沒啥關係 XD 這次的主要目的是為 CodiMD 做一個定期備份,另外為了儲存空間上的考量,備份檔案只保留 30 天,一旦超過就刪除資料。
不過太久沒重頭寫 Shell Script 了,連起手式都有點忘了 Orz,快速找鳥哥複習一下塵封在記憶中的起手式
咳,回頭正經的,這篇文章目的可以拆解成三個動作:
另外執行完備份,為了避免這台機器掛掉連同備份一起不見,最後在做一個異地的備份
接下來複製整個資料夾,不過因為權限問題,所以我 cp 指令前加上了 sudo,之後在用 chown 更改資料夾權限。
建議在使用 cp 指令時加上 -p,保留時間、擁有者…等資訊
因為這是針對 CodiMD 的備份,所以除了一般資料夾的複製外,我也執行了 CodiMD 的 Backup 指令。
PS. 這邊需要特別注意一下在 crontab 中使用 docker-compose 的一些坑…。 恩…,會這麼提醒當然是因為我踩過了 Orz … 我老是到處踩坑…
這邊使用 find 指令來實做。利用 find 找到符合特定規則的檔案後再使用指令參數 -exec 來刪除。這邊所指的特定規則,當然是指三十天前的壓縮檔:
指令可以分成兩部份:
~/codimd_backup
: 要尋找的目標路徑-type f
: 尋找類型為檔案-name "*.tgz"
: 尋找檔名為 .tgz 結尾-mtime +30
:檔案的最後修改時間是 30 天以前的
exec 後面的 command 是要執行的指令,這執行刪除指令:
其中 {} 指的是 find 指令找到的檔案或目錄,而 ; 是指令的結束符號。
Script 寫完之後最後就是讓它定期執行了,這邊直接使用 crontab 指令即可,設定方式還算是單純,只是需要稍微注意一下執行者權限與絕對路徑…等等
此時會進入 vi 的編輯畫面,就可以進行工作的排程。注意每一行都是一項工作排程。而每項工作的格式都是由六個欄位所組成:
代表意義 | 分鐘 | 小時 | 日期 | 月份 | 週 | 指令 |
---|---|---|---|---|---|---|
數字範圍 | 0-59 | 0-23 | 1-31 | 1-12 | 0-7 | 就指令啊 |
寫起來會像是這樣
意思就是每天 0 點時執行 codimd_backup.sh 這個 Script。
原本我的想法很簡單,用 scp 將備份的檔案從 CodiMD 的伺服器(簡稱 ServerA)送到備份用的伺服器(簡稱 ServerB)就好。偏偏遇到一個有點尷尬的問題,ServerA 在外網,而 ServerB 在內網,兩邊是互相 ping 不到的。最後沒辦法只好先貢獻我的電腦當跳板 (´_ゝ`)
所以整體步驟就變成需要先 Download 再 Upload 了:
我一樣先建立一個資料夾,等等放從 CodiMD 的伺服器抓下來的備份
然後進行下載
這邊每天下載最新的備份就好,所以這邊使用了 ls 並使檔案依照修改時間降序排序, 最後使用使用 head 取出排序中的第一筆資料,也就是最新的資料,最後使用 scp 將資料抓下來,指令一樣建議加上 -p 保留 timestamps 資訊。
最後再將抓下來資料送到 ServerB
這邊麻煩的一點是,ServerB 是用密碼登入的…,所以我還得寫一個自動登入的 Script,只好邊查 expect 的語法邊湊出一版。
最後我一樣希望 ServerB 備儲檔案別無限制儲存下去,所以我一樣希望它刪除 30 天前(或許 60 天?)的檔案。這邊我稍微猶豫了一下這件事是要在跳板這台機器下 Script ,還是在 ServerB 另外起一個 Script ,最後我決定偷懶跟著目前的 Script 一起做。
這指令與先前的差不多唯一需要注意的是在 spawn 中需要注意字元的跳脫,不然就會跟我一樣 de 了好久的 bug …
後來雖然抓完蟲,不過還是一氣之下把 ServerB 的改成了使用 SSH Key-based 的登入驗證方式,所以程式可以化簡成:
後來想到一件事,我的電腦偶而會關機,所以為了以防異地備份的檔案有少,將 Download 最新的檔案,改成 Download 不在 ServerB 中的所有檔案。
一般來說,如果要找可以 diff -q ,可以列出哪些檔案只存於 folderA ,哪些檔案又只存於 folderB。取出只存於 folderA 檔案,再用 awk 取出檔名的部份,最後寫檔。
只是因為目前我的資料夾存在兩台不同的伺服器上,所以必須用到 process substitution ,但 process substitution 是拿來暫存檔案用的,如果我用 diff -q 會行不通,所以稍微換了個方式實做:
先使用 process substitution 分別列出兩台伺服器上的檔案並排序,然後使用 diff 比較兩份暫存檔的內容,濾出僅存於 ServerA 檔案,再用 awk 取出檔名的部份,最後寫檔。
最後在依序從檔案中讀出要下載的檔名,並依序下載:
對了,使用 process substitution 時,要改用 bash 來執行 Script ,不能用 sh,不然會一直收到錯誤訊息:
Syntax error: "(" unexpected
本文作者: 辛西亞.Cynthia
本文連結: 辛西亞的技能樹 / hackmd 版本
版權聲明: 部落格中所有文章,均採用 姓名標示-非商業性-相同方式分享 4.0 國際 (CC BY-NC-SA 4.0) 許可協議。轉載請標明作者、連結與出處!