私は必要です
cronで使用して夜間に無人で実行して、すべての最初の月のバックアップと最新の14のバックアップを除くすべてのバックアップディレクトリを削除するスクリプト(bash?)を作成するのに問題があります。 bashが機能しない場合は移植性が必要なので、シェルとPOSIXを使用できます。
スクリプトは安全でエレガントでなければなりません。どこに付いてるの?:5月以降のバックアップが発生していないことを認識し、スクリプトが11月に実行されたにもかかわらず、最新(5月)の14個のバックアップが最新であるため維持します。すべての場合において、スクリプトは、名前の日付部分が01のすべてのバックアップ(-YYYYMMDD-)を維持する必要があります。
私が所有しているもの
- バックアップを含むDIRSがあります。
- バックアップ日は DIR 名にあります。
- スクリプトはコンテンツを読み取り、
/path/to/backups/example.com/
コンテンツから削除するDIRSを決定する必要があります。 - DIRSは空ではありません。これには今日のバックアップが含まれます。
/path/to/backups/example.com/example.com-20210101-backup/ // Keep (first of month)
/path/to/backups/example.com/example.com-20210201-backup/ // Keep (first of month)
/path/to/backups/example.com/example.com-20210301-backup/ // Keep (first of month)
/path/to/backups/example.com/example.com-20210401-backup/ // Keep (first of month)
/path/to/backups/example.com/example.com-20210501-backup/ // Keep (first of month)
/path/to/backups/example.com/example.com-20210502-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210503-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210504-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210505-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210506-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210507-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210508-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210509-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210510-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210511-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210512-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210513-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210514-backup/ // <-- Script to remove
/path/to/backups/example.com/example.com-20210515-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210516-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210517-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210518-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210519-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210520-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210521-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210522-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210523-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210524-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210525-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210526-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210527-backup/ // Keep (Most recent 14 days even if old)
/path/to/backups/example.com/example.com-20210528-backup/ // Keep (Most recent 14 days even if old)
なぜこれをスクリプトで書くのですか?
これは、バックアップが復元され、自動的に削除する必要がある新しいDIRSがある可能性があるためです。
私が見つけたもの
私が見つけたすべては、最後の14を除くすべてのアイテムを削除したり、最初の月だけを維持したり、両方を維持したりしませんでした。
たとえば、次のようになります。https://unix.stackexchange.com/a/379041
find /path/to/backups/example.com/ -type d -mtime +14 -exec rm -rf {} +
スクリプトが実行された日付から最新の14個のスクリプトのみが表示され、11月に実行されると何も表示も削除もされません。
私の問題
すべての最初の月のバックアップと最新の14のバックアップ、さらには古いバックアップまで維持するセキュリティスクリプトを生成する方法はありません。
正しい方向にご案内いただきありがとうございます。
-ありがとう!
答え1
仮定:
- バックアップディレクトリ(少なくとも最上位レベル)は、スペース/改行/正規表現、またはシェルglobメタ文字なしでうまく構成されています。
- パスにディレクトリのコレクションがあります。
base_path
- すべてのディレクトリはプレフィックスで始まります。
base_prefx
- 各ディレクトリはサフィックスで終わります。
base_suffx
- パス、プレフィックス、およびサフィックスが削除された場合、各ディレクトリ名は日付です。
YYYYMMDD
- これらの基準を満たさないディレクトリは無視されます。
与えられた情報に基づいて、それに応じて戦略を計画できます。
現在の作業の鍵は、YYYYMMDD
名前の一部に基づいてゼロ個以上のディレクトリを削除することです。削除する特定のディレクトリ(存在する場合)を決定するには、次の手順を実行します。
DD
01
日付部分があるか、フィールドに数字以外の文字が予想されるすべてのディレクトリを除外します。YYYYMMDD
- 残りのディレクトリでは、
N
最新の日付を除外します。 - 残りのすべてのディレクトリ(存在する場合)が削除されます。
あなたはを選択しましたN=14
。
#!/usr/bin/env bash
retain=14
base_path='./path/to/backups/example.com/'
base_prefx='example.com-'
base_suffx='-backup'
find "$base_path" -maxdepth 1 -mindepth 1 \
-type d \
-name "${base_prefx}????????${base_suffx}" |
while IFS= read dir
do
base="$(basename "$dir" "$base_suffx")"
printf '%s\n' "${base#$base_prefx}"
done |
grep -Ev '([^[:digit:]]|01$)' |
sort -r |
tail +$(($retain+1)) |
while IFS= read base
do
printf 'rm -rf "%q%q%q%q"\n' \
"$base_path" "$base_prefx" "$base" "$base_suffx"
done
このfind
コマンドは、base_path
仮想ディレクトリ構造テンプレートと一致し、そのディレクトリよりも正確に1つ下のディレクトリにあるサブディレクトリ名を見つけますbase_path
。
find
の出力は、入力の各行を読み取って削除し、ディレクトリbase_path
名(表面的には日付)部分を書き込むwhileループに供給されます。base_prefx
base_suffx
base
stdout
次にstdout
それを渡して、grep
数字以外の文字を含むすべての項目を削除します。またはで終わる項目は、毎月1日のバックアップが無期限に保持01
されるように削除することが01
重要です。
grep
次に出力をsort
入力します。削減したがって、最新の項目(すべての??????01
項目を除く)が出力の上部にあり、最新の項目が出力の上部に表示されます。
すべての??????01
バックアップディレクトリの日付を除いて、日付を最新の日付から降順に並べ替えたので、残りの作業は最初の項目をスキップしてそれ以上のすべてのN
項目を削除することです。N+1
コードは変数を使用してretain
表されますN
。 ed出力をtail
読み取り、sort
ラインから始まるライン出力を開始retain+1
し、ストリームがループstdout
に渡されます。while
ループは各行を変数として読み取って参照しbase
、それに続くrm -rf
コマンドを再構築してからそのコマンドを作成します。base_path
base_prefx
base
base_suffx
stdout
rm
コマンドは書き込みのみなので、stdout
スクリプトは何も削除しません。出力を操作する前に、出力の精度を確認する必要があります。コマンドが正しく表示されたら、出力をパイプしてsh
コマンドrm
を実行できます。スクリプトを十分にテストしたら、printf
実際に正しいrm -rf
コマンドを呼び出して行を通過するように行を変更できますcron
。
テストするディレクトリを作成しましょう。
mkdir -p path/to/backups/example.com/example.com-20210101-backup
mkdir -p path/to/backups/example.com/example.com-20210201-backup
mkdir -p path/to/backups/example.com/example.com-20210301-backup
mkdir -p path/to/backups/example.com/example.com-20210401-backup
mkdir -p path/to/backups/example.com/example.com-20210501-backup
mkdir -p path/to/backups/example.com/example.com-20210502-backup
mkdir -p path/to/backups/example.com/example.com-20210503-backup
mkdir -p path/to/backups/example.com/example.com-20210504-backup
mkdir -p path/to/backups/example.com/example.com-20210505-backup
mkdir -p path/to/backups/example.com/example.com-20210506-backup
mkdir -p path/to/backups/example.com/example.com-20210507-backup
mkdir -p path/to/backups/example.com/example.com-20210508-backup
mkdir -p path/to/backups/example.com/example.com-20210509-backup
mkdir -p path/to/backups/example.com/example.com-20210510-backup
mkdir -p path/to/backups/example.com/example.com-20210511-backup
mkdir -p path/to/backups/example.com/example.com-20210512-backup
mkdir -p path/to/backups/example.com/example.com-20210513-backup
mkdir -p path/to/backups/example.com/example.com-20210514-backup
mkdir -p path/to/backups/example.com/example.com-20210515-backup
mkdir -p path/to/backups/example.com/example.com-20210516-backup
mkdir -p path/to/backups/example.com/example.com-20210517-backup
mkdir -p path/to/backups/example.com/example.com-20210518-backup
mkdir -p path/to/backups/example.com/example.com-20210519-backup
mkdir -p path/to/backups/example.com/example.com-20210520-backup
mkdir -p path/to/backups/example.com/example.com-20210521-backup
mkdir -p path/to/backups/example.com/example.com-20210522-backup
mkdir -p path/to/backups/example.com/example.com-20210523-backup
mkdir -p path/to/backups/example.com/example.com-20210524-backup
mkdir -p path/to/backups/example.com/example.com-20210525-backup
mkdir -p path/to/backups/example.com/example.com-20210526-backup
mkdir -p path/to/backups/example.com/example.com-20210527-backup
mkdir -p path/to/backups/example.com/example.com-20210528-backup
mkdir -p path/to/backups/example.com/example.com-20210228-backup/example.com-20210101-backup
mkdir -p path/to/backups/example.com/example.com-messedup-backup/example.com-20210227-backup
mkdir -p path/to/backups/example.com/example.com-20210428-backup/example.com-20210601-backup
次にスクリプトを実行します。
$ ./test.sh
rm -rf "./path/to/backups/example.com/example.com-20210514-backup"
rm -rf "./path/to/backups/example.com/example.com-20210513-backup"
rm -rf "./path/to/backups/example.com/example.com-20210512-backup"
rm -rf "./path/to/backups/example.com/example.com-20210511-backup"
rm -rf "./path/to/backups/example.com/example.com-20210510-backup"
rm -rf "./path/to/backups/example.com/example.com-20210509-backup"
rm -rf "./path/to/backups/example.com/example.com-20210508-backup"
rm -rf "./path/to/backups/example.com/example.com-20210507-backup"
rm -rf "./path/to/backups/example.com/example.com-20210506-backup"
rm -rf "./path/to/backups/example.com/example.com-20210505-backup"
rm -rf "./path/to/backups/example.com/example.com-20210504-backup"
rm -rf "./path/to/backups/example.com/example.com-20210503-backup"
rm -rf "./path/to/backups/example.com/example.com-20210502-backup"
rm -rf "./path/to/backups/example.com/example.com-20210428-backup"
rm -rf "./path/to/backups/example.com/example.com-20210228-backup"
よさそうですね。実行してみましょう。
$ ./test.sh | sh
修正する
ファイル名にシェルグローバル変数(例????????
:)と正規表現(例:)を混在させると、[0-9]{6}Z
扱いにくい場合があります。もちろん、スクリプトを完全に正規表現を使用するように調整することもできますが、そうすると少し複雑になります。
#!/usr/bin/env bash
retain=15
# This is a shell glob (with no wildcards); must end in slash
base_path='./path/to/backups/example.com/'
# This is an extended regex pattern:
base_regex='\./path/to/backups/example\.com/example\.com-([0-9]{8}-[0-9]{6}Z)-backup'
# This is a printf spec to printf a base_path and a date-time to a full directory name:
printf_spec='%qexample.com-%q-backup'
find -E "$base_path" -maxdepth 1 -mindepth 1 \
-type d \
-regex "${base_regex}" |
sed -Ee "s~^${base_regex}$~\1~" |
grep -Ev '^[0-9]{6}01-' |
sort -r |
tail -n +$(($retain+1)) |
while IFS= read line
do
printf "rm -rf ${printf_spec}\n" "${base_path}" "$line"
done
どの変数がシェルグローバル変数であるか、正規表現であるか、仕様であるかを明確にするために、上部にコメントが追加されましたprintf
。これは次の理由で必要です。
base_path
必要なのは、find
どこを見なければならないかを伝えるシェルグローブです。base_regex
find ... -regex
完全行(ディレクトリ名)と一致する正規表現が必要なので、完全行正規表現でなければなりません。正規表現文字は、.
文字が現れるたびにエスケープされます。printf_spec
printf
文字列YYYYMMDD-HHMMSSZ
を有効なディレクトリ名にフォーマットする互換仕様が必要です。
これで、名前がfind -E
拡張された正規表現(ala)と$base_path
完全行マッチングを形成するこのディレクトリの下で、正確に1つ下のディレクトリを見つけるように指示し、指示できます。grep -Ex
$base_regex
一致させる正規表現部分はYYYYMMDD-HHMMSSZ
括弧で囲まれています。これにより、sed
次のステップで役に立つ「逆参照」が作成されます。find
toの出力全体を渡し、入力の各行を時系列でソートする必要がある正規表現のsed
括弧部分と一致する行の一部に置き換えるように指示します。YYYYMMDD-HHMMSSZ
以前のスクリプトはタイムスタンプを解析するためにbash-ismを使用していましたが、bash-ismはglobに依存しているため、正規表現ベースのソリューションを実装するためにsed
。
スクリプトの残りの部分は基本的に同じです。つまり、毎月1日にすべてのバックアップジョブを削除するようにsed
出力が渡されます。grep
出力は逆順に進み、sort
リストの上部の最大値をtail
スキップし、その後の各行を各行を渡すwhileループに出力します。$retain
printf
指示:
経験豊富なU&Lユーザーは他のことを指摘することもできますが、以下は参考にするいくつかの点です。
base_regex
ディレクトリ名リテラルと一致すると予想される使用する正規表現文字をエスケープします。- この
sed
コマンドは、~
検索と置換の区切り文字として a を使用します。したがって、ディレクトリ名にチルダを使用しないでください。base_regex
文字列にチルダを追加しない限り、find
これらのディレクトリはファイルシステムに実際に作成された場合でも削除する必要があります。 - このアルゴリズムは各日付/時刻の組み合わせを一意のバックアップとして扱うため、昨日14のバックアップジョブが実行された場合、最後の14のバックアップの保持は昨日のバックアップのみを保持できます。
答え2
@ジムありがとうございます。これは、ディレクトリ名にZulu時間を追加する必要があるまで機能し、次のようにしました。
./path/to/backups/example.com/example.com-20210101-040538Z-backup
ノート:ジュール時間は毎日変わります。
これはそれを壊す。
だから私はこのような時間文字列を解決するために正規表現を追加するスクリプトに従いました。しかし、私の正規表現形式に問題があると確信しているので、次は機能しません。
#!/usr/bin/env bash
retain=15
base_path='./path/to/backups/example.com/'
base_prefx='example.com-'
base_time="-040538Z" # <--- Works but is not Regex.
# base_time="-[[:digit:]]{6}Z" # <--- Regex (I think) but not working.
base_suffx="-backup"
find "$base_path" -maxdepth 1 -mindepth 1 \
-type d \
-name "${base_prefx}????????${base_time}${base_suffx}" |
while IFS= read dir
do
base="$(basename "$dir" "$base_time$base_suffx")"
printf '%s\n' "${base#$base_prefx}"
done |
grep -Ev '([^[:digit:]]|01$)' |
sort -r |
tail -n +$(($retain+1)) |
while IFS= read base
do
printf 'success-safety-rm -rf "%q%q%q%q%q"\n' \
"$base_path" "$base_prefx" "$base" "$base_time" "$base_suffx"
done
たぶん私がやっていることが間違っているのではないでしょうか?
このスクリプトをどのように設定できますか?
答え3
GNU導入ソリューション
子孫への注意:@Jim L上記の許可された回答の更新された部分は、ソリューションであり基礎です。
なぜ一人で答えますか?
find -E
上記の「Accepted Answer Update」セクションがGNU環境でエラーを引き起こすためですfind: unknown predicate '-E'
。
以下は、GNUベースのLinux用の作業スクリプトです。
#!/usr/bin/env bash
retain=15
# CHANGEME This is a shell glob (with no wildcards); must end in slash; example:
base_path='./path/to/backups/example.com/'
# CHANGEME This is an extended regex pattern example:
base_regex='\./path/to/backups/example\.com/example\.com-([0-9]{8}-[0-9]{6}Z)-backup'
# CHANGEME This is a printf spec to printf a base_path and a date-time to a full directory name example:
printf_spec='%qexample.com-%q-backup'
find "$base_path" -maxdepth 1 -mindepth 1 \
-type d \
-regextype posix-extended \
-regex "${base_regex}" |
sed -Ee "s~^${base_regex}$~\1~" |
grep -Ev '^[0-9]{6}01-' |
sort -r |
tail -n +$(($retain+1)) |
while IFS= read line
do
printf "REMOVEME-SAFETY-rm -rf ${printf_spec}\n" "$base_path" "$line"
done
走る:
./test.sh
結果:
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20211105-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20211104-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20211103-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20211102-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210303-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210302-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210203-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210202-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210110-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210109-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210108-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210107-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210106-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210105-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210104-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210103-040538Z-backup
REMOVEME-SAFETY-rm -rf /path/to/backups/example.com/example.com-20210102-040538Z-backup
example.comテスト設定から削除するバックアップディレクトリは何ですか?
これでprintf "REMOVEME-SAFETY-rm...
、無人で実行され、実際にディレクトリを削除するように行を調整できます。
メモ:荷物指示:上記で許可された回答もこのバージョンを表します。
@Jim L ありがとうございます。
答え4
別のファイル命名パターンを使用してさまざまなバックアップを削除しようとしたため、他のソリューションは私のシナリオでは確実に機能しませんでした。そのため、ファイル名ではなくファイルの作成(または変更)日に基づいて次のスクリプトを作成しました。
#!/usr/bin/env bash
dryRun=true # Set to `false` or remove this line to move from logging to deletion
rootFolder="/var/lib/psa/dumps/" # Files within this folder will be checked recursively
fileGlob="*.*" # Limit to specific file type if required
fileAgeLimit="30 days ago" # All files up to this age (date only and inclusive) will be kept
regularExpressionForDatesToBeKept='-[0-9][0-9]-01$' # This default regular expression will keep all files from the 1st of each month
checkForDeletion() {
filePath=$1
fileName=$(basename "$filePath")
fileDateTimeString=$(stat -c '%w' "$filePath") # Creation date; only available on newer file systems
if [[ "$fileDateTimeString" = "-" ]]; then
fileDateTimeString=$(stat -c '%y' "$filePath") # Use modification date instead
fi
fileDateString="$(date +"%Y-%m-%d" -d "$fileDateTimeString")"
fileAgeLimitDateString="$(date +"%Y-%m-%d" -d "$fileAgeLimit")"
if [[ "$fileDateString" < "$fileAgeLimitDateString" ]]; then
if [[ "$fileDateString" =~ $regularExpressionForDatesToBeKept ]]; then
[[ $dryRun = true ]] && echo -e "To be kept\tFile date: $fileDateString (matches '$regularExpressionForDatesToBeKept')\t$fileName"
else
if [[ $dryRun = true ]]; then
echo -e "To be DELETED\tFile date: $fileDateString\t\t\t\t$fileName"
else
rm -f "$filePath"
fi
fi
else
[[ $dryRun = true ]] && echo -e "To be kept\tFile date: $fileDateString ($fileAgeLimit or younger)\t$fileName"
fi
}
# Safely loop through all find matches
while read -r -d ''; do
checkForDeletion "$REPLY"
done < <(find "$rootFolder" -type f -name "$fileGlob" -print0)