Last active
December 12, 2025 21:30
-
-
Save mcisback/374c0c34e1564910954315245d50c642 to your computer and use it in GitHub Desktop.
rm protection and guards in bash
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Put this in your .bashrc | |
| # Load it with source ~/.bashrc | |
| rm() { | |
| local YES="false" | |
| local args=() | |
| local protected_dirs=( | |
| "/" | |
| "/Volumes" | |
| "/Applications" | |
| "/bin" | |
| "/cores" | |
| "/dev" | |
| "/etc" | |
| "/home" | |
| "/Library" | |
| "/opt" | |
| "/private" | |
| "/sbin" | |
| "/System" | |
| "/tmp" | |
| "/Users" | |
| "/usr" | |
| "/var" | |
| "$HOME" | |
| "$HOME/Desktop" | |
| "$HOME/Documents" | |
| "$HOME/Documents/TODO.txt_Private" | |
| "$HOME/Downloads" | |
| "$HOME/Pictures" | |
| "$HOME/Applications" | |
| "$HOME/Dev" | |
| "$HOME/Dropbox" | |
| "$HOME/.go" | |
| "$HOME/go" | |
| "$HOME/Library" | |
| "$HOME/Movies" | |
| "$HOME/Music" | |
| "$HOME/Parallels" | |
| "$HOME/Programs" | |
| "$HOME/Public" | |
| "$HOME/Test" | |
| ) | |
| for arg in "$@"; do | |
| if [[ "$arg" == "--yes" ]]; then | |
| YES="true" | |
| else | |
| args+=("$arg") | |
| fi | |
| done | |
| block() { | |
| echo "🚫 rm blocked" | |
| echo " Protected target: $1" | |
| echo " Use --yes if you are absolutely sure." | |
| # IMPORTANT: This doesn't stop the script and it must be stopped | |
| return 1 | |
| } | |
| # True if target is exactly: DIR/* DIR/. DIR/.. | |
| is_wipe_of_dir() { | |
| local target="$1" | |
| local dir="$2" | |
| [[ "$target" == "$dir"/* ]] && [[ "${target#$dir/}" == "*" ]] && return 0 | |
| [[ "$target" == "$dir"/ ]] && return 0 | |
| [[ "$target" == "$dir/." || "$target" == "$dir/.." ]] && return 0 | |
| return 1 | |
| } | |
| # Normalize path; keep glob literal if it contains '*' | |
| normalize() { | |
| local p="$1" | |
| if [[ "$p" == *"*"* ]]; then | |
| # Expand ~ but keep glob | |
| if [[ "$p" == "~/"* ]]; then | |
| echo "$HOME/${p#~/}" | |
| else | |
| echo "$p" | |
| fi | |
| else | |
| realpath -m -- "$p" 2>/dev/null || echo "$p" | |
| fi | |
| } | |
| STOP="false" | |
| for target in "${args[@]}"; do | |
| local resolved | |
| resolved="$(normalize "$target")" | |
| for dir in "${protected_dirs[@]}"; do | |
| local dnorm | |
| dnorm="$(normalize "$dir")" | |
| # 1) rm ... DIR -> block | |
| if [[ "$resolved" == "$dnorm" ]]; then | |
| if [[ "$YES" == "true" ]]; then | |
| STOP="true" | |
| break | |
| fi | |
| block "$resolved" | |
| # STOP THE SCRIPT | |
| # Cannot use exit as it closes the shell | |
| return 1 | |
| fi | |
| if is_wipe_of_dir "$resolved" "$dnorm"; then | |
| if [[ "$YES" == "true" ]]; then | |
| STOP="true" | |
| break | |
| fi | |
| block "$resolved" | |
| return 1 | |
| fi | |
| done | |
| if [[ "$STOP" == "true" ]]; then | |
| break | |
| fi | |
| done | |
| echo "✅ Removing ${args[@]}" | |
| command /bin/rm "${args[@]}" | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment