修正する

修正する

私は必要です

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名前の一部に基づいてゼロ個以上のディレクトリを削除することです。削除する特定のディレクトリ(存在する場合)を決定するには、次の手順を実行します。

  • DD01日付部分があるか、フィールドに数字以外の文字が予想されるすべてのディレクトリを除外します。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_prefxbase_suffxbasestdout

次に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_pathbase_prefxbasebase_suffxstdout

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_regexfind ... -regex完全行(ディレクトリ名)と一致する正規表現が必要なので、完全行正規表現でなければなりません。正規表現文字は、.文字が現れるたびにエスケープされます。
  • printf_specprintf文字列YYYYMMDD-HHMMSSZを有効なディレクトリ名にフォーマットする互換仕様が必要です。

これで、名前がfind -E拡張された正規表現(ala)と$base_path完全行マッチングを形成するこのディレクトリの下で、正確に1つ下のディレクトリを見つけるように指示し、指示できます。grep -Ex$base_regex

一致させる正規表現部分はYYYYMMDD-HHMMSSZ括弧で囲まれています。これにより、sed次のステップで役に立つ「逆参照」が作成されます。findtoの出力全体を渡し、入力の各行を時系列でソートする必要がある正規表現のsed括弧部分と一致する行の一部に置き換えるように指示します。YYYYMMDD-HHMMSSZ以前のスクリプトはタイムスタンプを解析するためにbash-ismを使用していましたが、bash-ismはglobに依存しているため、正規表現ベースのソリューションを実装するためにsed

スクリプトの残りの部分は基本的に同じです。つまり、毎月1日にすべてのバックアップジョブを削除するようにsed出力が渡されます。grep出力は逆順に進み、sortリストの上部の最大値をtailスキップし、その後の各行を各行を渡すwhileループに出力します。$retainprintf

指示:

経験豊富な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)

関連情報