Bash
為什麼這個腳本很少工作?
我有以下 bash 腳本在 OpenMediaVault 上作為 cronjob 執行:
BACKUP_DIR='/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/' BACKUP_FILE_PATH="/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/Backup [ashen] ($(date +%d-%m-%Y)).tar.gz" SERVER_DIR=/var/lib/docker/volumes/49c9e5c53ea5b9c893c0a80117860da9b493484395c0$ MAX_BACKUPS_COUNT=4 tar -zcf "$BACKUP_FILE_PATH" $SERVER_DIR cd "$BACKUP_DIR" [[ $( ls | wc -l ) -gt $MAX_BACKUPS_COUNT ]] && rm "$(ls -t | tail -1)"
該腳本的重點是在給定位置創建一個 .tar.gz 備份,如果備份目錄中的文件超過 4 個,則刪除最舊的文件(重點是只保留 4 個最近的備份)。最後一行/命令並不總是有效。在終端中手動執行它可以按預期工作,有時腳本會執行它,但有時它會停止,直到我手動嘗試執行腳本/行,然後它似乎神奇地修復了一段時間。
有誰知道為什麼它偶爾會停止執行最後一行?即使我看到正在創建備份。
如果任何文件名包含換行符,您的腳本將失敗。解析
ls
是一個非常糟糕的主意,很可能會失敗。此外,您的腳本只會刪除最新的文件。因此,如果有 100 個文件,您將剩下 99 個。您似乎期望它會刪除除最近的 4 個之外的所有內容,但腳本的邏輯不是這樣工作的。這是一種替代方法,可以處理任意文件名並實際上刪除除了最近的 4 個文件之外的所有文件:
#!/bin/bash ## avoid using CAPS for local variable names in shell scripts backup_dir='/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/' backup_file_path="/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/Backup [ashen] ($(date +%d-%m-%Y)).tar.gz" server_dir='/var/lib/docker/volumes/49c9e5c53ea5b9c893c0a80117860da9b493484395c0$' ## This needs to be set to the number of files you want to keep plus one, ## so that we can use tail -n $max_backups below. max_backups=5 tar -zcf "$BACKUP_FILE_PATH" "$SERVER_DIR" ## delete all but the newest 4 tar.gz files in the ## backup directory stat --printf '%Y %n\0' "$backup_dir"/*tar.gz | sort -rznk1,1 | tail -z -n +"$max_backups" | sed -z 's/^[0-9]* //' | xargs -0 rm -v
這裡的工作是由該
stat
命令和各種下游管道完成的。以下是該命令正在執行的操作的細分:
stat --printf '%Y %n\0' "$backup_dir"/*tar.gz``.tar.gz
:這將列印備份目錄中所有文件的紀元以來的文件名和文件年齡(以秒為單位)。為了能夠處理帶有換行符 (\n
) 的文件名,我們需要以 NULL (\0
) 結束每個條目。這是輸出的樣子:$ stat --printf '%Y %n\0' * | tr '\0' '\n' 1616867929 ./afile 5 tar.gz 1616868565 ./file 10 tar.gz 1616868560 ./file 1 tar.gz 1616868561 ./file 2 tar.gz 1616867927 ./file 3 tar.gz 1616867928 ./file 4 tar.gz 1616867930 ./file 6 tar.gz 1616868562 ./file 7 tar.gz 1616868563 ./file 8 tar.gz 1616868564 ./file 9 tar.gz
對於此範例,我將輸出通過管道傳輸到
tr '\0' '\n'
以使其清晰易讀,但在實際輸出中,每條記錄的末尾都有一個\0
。
sort -rznk1,1
:stat
上面的輸出通過管道傳送到sort
它將按數字排序(-n
),以相反的順序(-r
),\0
用作記錄分隔符(-z
)並且只考慮第一個欄位(-k1,1
),這是文件的年齡。輸出如下所示:
$ stat --printf '%Y %n\0' "$backup_dir"/*tar.gz | sort -rznk1,1 | tr '\0' '\n' 1616868565 ./file 10 tar.gz 1616868564 ./file 9 tar.gz 1616868563 ./file 8 tar.gz 1616868562 ./file 7 tar.gz 1616868561 ./file 2 tar.gz 1616868560 ./file 1 tar.gz 1616867930 ./file 6 tar.gz 1616867929 ./afile 5 tar.gz 1616867928 ./file 4 tar.gz 1616867927 ./file 3 tar.gz
tail -z -n +"$max_backups"
:該命令tail -n +X
將列印您從記錄開始給它的最後一條記錄X
。這裡X
是$max_backups
變數,這就是為什麼需要將該變數設置為您要保留的文件數加一的原因。讓-z
我們tail
處理以空結尾的記錄。此時,我們有了要刪除的文件列表,但它們也有它們的年齡,我們需要將其刪除:
$ stat --printf '%Y %n\0' "$backup_dir"/*tar.gz | sort -rznk1,1 | tail -z -n +5 | tr '\0' '\n' 1616868561 ./file 2 tar.gz 1616868560 ./file 1 tar.gz 1616867930 ./file 6 tar.gz 1616867929 ./afile 5 tar.gz 1616867928 ./file 4 tar.gz 1616867927 ./file 3 tar.gz
sed -z 's/^[0-9]* //'
:刪除文件的年齡,只留下名稱。再一次,-z
處理空終止記錄:$ stat --printf '%Y %n\0' "$backup_dir"/*tar.gz | sort -rznk1,1 | tail -z -n +5 | sed -z 's/^[0-9]* //' | tr '\0' '\n' ./file 2 tar.gz ./file 1 tar.gz ./file 6 tar.gz ./afile 5 tar.gz ./file 4 tar.gz ./file 3 tar.gz
xargs -0 rm -v
: 最後一步。這將刪除文件,並再次刪除文件,-z
以便它可以處理以空值結尾的記錄。重要提示:該腳本假定您使用的是 GNU 工具。Open Media Vault聲稱是 Linux 並執行 Debian,所以它應該適合你,但我從未使用過那個系統,所以我不能確定。