Thuta Learning
Bash / Shell Scripting
ProjectsDevOps & Toolsbeginner

ပရောဂျက် — Rotation ပါသော အလိုအလျောက် Backup Script

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • ပရောဂျက် — Rotation ပါသော အလိုအလျောက် Backup Script concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • နမူနာ code ကို ကိုယ်တိုင် run ပြီး output စစ်နိုင်ရန်
  • Tutorial Platform project နှင့် production scenario တွင် မှန်ကန်စွာအသုံးချနိုင်ရန်

နားလည်ထားရမယ့် အချက်

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 များအပေါ်တွင်သာ မှီခိုနေပြီး ၎င်းတို့ကို ဘယ်လို ဖန်တီးခဲ့သလဲဆိုတာကို လုံးဝ မမှီခိုသောကြောင့်ဖြစ်သည်။

အတူတူ စမ်းရေးကြည့်မယ်

bash
#!/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"
You should see
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 ManualBash / Shell Scripting

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • 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 ကိုမူ မပြောင်းလဲသောကြောင့်ဖြစ်သည်။
  • နမူနာ code ကို production system ပေါ် တိုက်ရိုက်မစမ်းဘဲ local/test environment တွင် အရင်အတည်ပြုပါ။

လေ့ကျင့်ခန်း

count အသေအချာထားမည့်အစား N ရက်ထက်ကြီးသော backup များကို find ၏ -mtime flag ဖြင့် ဖျက်ပေးမည့် rotation mode ဒုတိယတစ်ခု ထပ်ထည့်ပြီး၊ caller က command-line flag ဖြင့် mode နှစ်ခုကြားမှ ရွေးချယ်နိုင်စေပါ။

You'll know it worked when: 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

ပရောဂျက် — Rotation ပါသော အလိုအလျောက် Backup Script | Thuta Learning