
OpenMediaVaultでcronjobとして実行される次のbashスクリプトがあります。
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つだけを維持することが重要です)。最後の行/コマンドが常に機能するわけではありません。端末で手動で実行すると期待どおりに機能し、時にはスクリプトが実行されますが、時にはスクリプト/行を手動で実行しようとするまで停止し、しばらくの間魔法のようにそれ自体が変更されるように見えます。
時々最後の行の実行が中断される理由を知っている人はいますか?バックアップが作成されたことを確認しましたが、同様です。
答え1
ファイル名に改行文字が含まれていると、スクリプトは失敗します。解析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
:バックアップディレクトリにあるすべてのファイルのepoch以降のファイル名とファイル寿命(秒)を印刷します。改行()でファイル名を処理するには、各エントリをNULL()で終了する\n
必要があります。\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'
ますが、実際の出力では各レコードの末尾に1つずつあります\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
Recordから始まり、最後に指定したレコードを印刷しますX
。ここに変数がX
あるので、$max_backups
保持するファイルの数に1を加えた値に変数を設定する必要があります。 null で終わるレコードを-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
null で終わるレコードを処理します。$ 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
NULL 終了レコードを処理できるように再度削除されます。
重要:このスクリプトはGNUツールを使用していると想定しています。メディアライブラリを開くLinuxと主張してDebianを実行しているので、あなたに役立ちます。しかし、私はそのシステムを使ったことがないので、確信できません。