နားလည်ထားရမယ့် အချက်
backup script တစ်ခုသည် အလုပ်တစ်ဝက်သာ ဖြစ်သည် — rotation policy မပါလျှင် backup များသည် disk space ပြည့်သွားသည့်အထိ ဆက်တိုက်စုပုံလာပြီး၊ များသောအားဖြင့် restore တစ်ခု အမှန်တကယ်လိုအပ်လာသည့်အချိန်တွင်ပင် ဆိုးဆိုးဝါးဝါး ပြင်ဆင်ရလေ့ရှိသည်။ ဤပရောဂျက်သည် rotation ကို rotate_backups() ဆိုသည့် self-contained function တစ်ခုအဖြစ် တည်ဆောက်ပြီး၊ ၎င်းကို လုံခြုံစွာ လုပ်ဆောင်နိုင်ရန် အခန်းရှေ့ပိုင်းများမှ concept များစွာကို ပေါင်းစပ်ထားသည်။ mapfile -t သည် ls ရဲ့ output ကို filename တစ်ခုချင်းစီအတွက် array element တစ်ခုအဖြစ် backups array ထဲသို့ ဖတ်ယူပေးသည် — for f in $(ls ...) ကဲ့သို့ word-splitting loop ရိုးရိုးထက် ပိုလုံခြုံသည်၊ ဘာကြောင့်ဆိုသော် filename ထဲတွင် space ပါခဲ့လျှင်တောင် element တစ်ခုလုံးအနေနှင့် ဖမ်းယူနိုင်သောကြောင့်ဖြစ်သည်။ listing ကို subshell (cd "$dir" && ls ...) အတွင်း run ထားခြင်းဖြင့် parent script ရဲ့ working directory ကိုယ်တိုင် side effect အဖြစ် ဘယ်တော့မှ ပြောင်းသွားမည်မဟုတ်ဘဲ၊ filename များလည်း directory prefix မပါဘဲ ပြန်ရရှိသည်။ array length ${#backups[@]} နှင့် arithmetic comparison (( total <= keep )) တို့က ဘာမှလုပ်စရာလိုမလို ဆုံးဖြတ်ပေးသည် — backup အရေအတွက်က keep limit ကို မကျော်လွန်သေးလျှင် function ကို safe no-op တစ်ခုအဖြစ် ဖြစ်စေမည့် conditional guard တစ်ခုပင်ဖြစ်သည်။ rotation လိုအပ်လာသောအခါ C-style for loop သည် array ထဲသို့ index (backups[$i]) ဖြင့် ဝင်ရောက်ကာ အဟောင်းဆုံး delete_count ခုကိုသာ တိတိကျကျ ဖျက်ပြီး ကျန်တာများကို မထိမခိုက်ဘဲ ချန်ထားသည်။ ဒါအားလုံးက အလုပ်ဖြစ်နေတာက file name များတွင် YYYYMMDD-HHMMSS timestamp encode လုပ်ထားလို့သာဖြစ်သည် — plain string အနေနှင့် sort လုပ်လိုက်ရုံနှင့် ထို format က အချိန်ကြောင်းအတိုင်းပါ sort ဖြစ်သွားသောကြောင့်၊ script ထဲတွင် ဘယ်နေရာမှ date parsing လုပ်ရန် လိုအပ်တော့ပါ — bug အမျိုးအစားတစ်ခုလုံးကို ဖယ်ရှားပေးသည့် design ရွေးချယ်မှုလေးတစ်ခုပင်ဖြစ်သည်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
database တစ်ခု သို့မဟုတ် project directory တစ်ခုကို backup လုပ်ပေးသည့် nightly cron job သည် shell scripting ၏ လက်တွေ့သုံးမှုအများဆုံးထဲမှ တစ်ခုဖြစ်သော်လည်း၊ ပထမဆုံးကြိုးစားမှုအများစုသည် rotation အဆင့်ကို လုံးဝမေ့လျော့ကြသည် — tarball အဟောင်းထောင်ချီ တိတ်တဆိတ်စုပုံလာသဖြင့် လနှစ်ခြောက်လကြာမှ server disk space ကုန်သွားသည်အထိ၊ တစ်ခါတစ်ရံ backup တွေက ကာကွယ်ပေးရမည့် service ကိုယ်တိုင်ကိုပါ ပျက်စီးစေတတ်သည်။ ဤ script ဘာလုပ်သည်ကို trace လုပ်ကြည့်ကြပါစို့— real backup run တစ်ခုက tar -czf ကို schedule အလိုက် ခေါ်မည်ဖြစ်သောကြောင့်၊ ဤနေရာရှိ setup ကမူ rotation logic ကို real disk ပေါ်တွင် ဖြစ်နေသလို မဟုတ်ဘဲ deterministic ဖြစ်ပြီး သိရှိပြီးသား input ဖြင့် run နိုင်ရန် fake, pre-timestamped file ငါးခုကို touch ဖြင့် seed ထားသည်။ rotate_backups ကို directory နှင့် KEEP=3 ဖြင့် ခေါ်သည်။ ၎င်းအတွင်းတွင် backup filename များကို list လုပ်ပြီး sort လုပ်ကာ၊ တွေ့ရှိသည့် count နှင့် name များကို print လုပ်ပြီးနောက် total <= keep ကို စစ်ဆေးသည် — ၅ ခုတွေ့ပြီး ၃ ခုထားရမည်ဖြစ်၍ rotation လိုအပ်သည်။ delete_count ကို ၂ အဖြစ် တွက်ချက်ပြီး index 0 နှင့် 1 ကို loop လှည့်သည် (array ကို အဟောင်းဆုံးမှစ sort ထားသောကြောင့် ဒါတွေက အဟောင်းဆုံးနှစ်ခုပင်ဖြစ်သည်)၊ တစ်ခုချင်းစီကို rm -f ဖြင့် ဖျက်ပြီး၊ နောက်ဆုံးတွင် ကျန်ရှိနေသည့်အရာများကို ပြန် list လုပ်ကာ အသစ်ဆုံးသုံးခုသာ ကျန်ရှိနေကြောင်း အတည်ပြုသည်။ production တွင်ဆိုလျှင် fake seed အဆင့်ကို real tar -czf ခေါ်ဆိုမှုနှင့် အစားထိုးလိုက်ရုံနှင့် ဤ script ကို အလုပ်လုပ်သော nightly backup job တစ်ခုအဖြစ် ပြောင်းလဲနိုင်သည် — အောက်ခြေက rotation logic ကိုမူ လုံးဝ ပြောင်းလဲစရာမလိုပါ၊ ဘာကြောင့်ဆိုသော် ၎င်းသည် file name များအပေါ်တွင်သာ မှီခိုနေပြီး ၎င်းတို့ကို ဘယ်လို ဖန်တီးခဲ့သလဲဆိုတာကို လုံးဝ မမှီခိုသောကြောင့်ဖြစ်သည်။
အတူတူ စမ်းရေးကြည့်မယ်
#!/usr/bin/env bash
set -euo pipefail
# --- Setup: simulate a backup directory that already has several backups ---
# In a real backup script these would be created by:
# tar -czf "backup-$(date +%Y%m%d-%H%M%S).tar.gz" /path/to/data
# Here we seed fixed, fake-timestamped files so the rotation logic below
# is 100% deterministic to demonstrate and verify.
BACKUP_DIR="demo_backups"
rm -rf "$BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
for ts in 20260101-000000 20260102-000000 20260103-000000 20260104-000000 20260105-000000; do
touch "$BACKUP_DIR/backup-${ts}.tar.gz"
done
KEEP=3
rotate_backups() {
local dir="$1"
local keep="$2"
# List backup files oldest-first (names sort correctly because the
# timestamp format is YYYYMMDD-HHMMSS, which sorts lexically = chronologically)
local -a backups
mapfile -t backups < <(cd "$dir" && ls backup-*.tar.gz | sort)
local total=${#backups[@]}
echo "Found $total backup(s) in $dir:"
for f in "${backups[@]}"; do
echo " $f"
done
echo ""
if (( total <= keep )); then
echo "Nothing to rotate: $total backup(s) <= keep limit of $keep."
return
fi
local delete_count=$(( total - keep ))
echo "Keeping newest $keep, deleting $delete_count oldest backup(s):"
for (( i=0; i<delete_count; i++ )); do
echo " DELETE ${backups[$i]}"
rm -f "$dir/${backups[$i]}"
done
echo ""
echo "Remaining backups after rotation:"
(cd "$dir" && ls backup-*.tar.gz | sort)
}
rotate_backups "$BACKUP_DIR" "$KEEP"Found 5 backup(s) in demo_backups:
backup-20260101-000000.tar.gz
backup-20260102-000000.tar.gz
backup-20260103-000000.tar.gz
backup-20260104-000000.tar.gz
backup-20260105-000000.tar.gz
Keeping newest 3, deleting 2 oldest backup(s):
DELETE backup-20260101-000000.tar.gz
DELETE backup-20260102-000000.tar.gz
Remaining backups after rotation:
backup-20260103-000000.tar.gz
backup-20260104-000000.tar.gz
backup-20260105-000000.tar.gz၅ မိနစ် စမ်းကြည့်
count အသေအချာထားမည့်အစား N ရက်ထက်ကြီးသော backup များကို find ၏ -mtime flag ဖြင့် ဖျက်ပေးမည့် rotation mode ဒုတိယတစ်ခု ထပ်ထည့်ပြီး၊ caller က command-line flag ဖြင့် mode နှစ်ခုကြားမှ ရွေးချယ်နိုင်စေပါ။
သတိလေးတစ်ချက်
keep count ကိန်းသင်္ချာကို (off-by-one အမှားများ) နှစ်ခါစစ်ဆေးခြင်းမပြုဘဲ wildcard သို့မဟုတ် loop ဖြင့် file ဖျက်ခြင်းသည် အဟောင်းဆုံးအစား အသစ်ဆုံး backup ကို တိတ်တဆိတ် ဖျက်မိနိုင်သည် — real rm ကိုမချိတ်ဆက်မီ dry-run echo ဖြင့် အမြဲအတည်ပြုပါ။
file name ထဲတွင် encode လုပ်ထားသော timestamp အစား file modification time (mtime) ကို အားကိုးလိုက်ခြင်းသည် backup တစ်ခုကို copy၊ restore သို့မဟုတ် process တစ်ခုခုက touch လုပ်လိုက်တာနှင့် ချက်ချင်းပျက်စီးသွားနိုင်သည် — copy လုပ်ခြင်းသည် mtime ကို reset လုပ်သော်လည်း name ကိုမူ မပြောင်းလဲသောကြောင့်ဖြစ်သည်။
GNU tar Manual — Bash / Shell Scripting