SC2115: Dangerous rm -rf with Unguarded Variables

Prevent catastrophic deletions from rm -rf with empty variables. Learn safe patterns using ${var:?}, set -u, and directory guards.

Error Handling

Detailed Explanation

The rm -rf Variable Trap

One of the most dangerous patterns in shell scripting is using rm -rf with a variable that might be empty or unset. An empty variable expands to nothing, potentially deleting far more than intended.

The Catastrophic Scenario

# If DEPLOY_DIR is unset or empty:
rm -rf $DEPLOY_DIR/*
# Becomes: rm -rf /*
# Deletes everything on the system!

rm -rf "$DEPLOY_DIR"/old
# Becomes: rm -rf ""/old -> rm -rf /old

Safe Patterns

Pattern 1: Use ${var:?}

rm -rf "${DEPLOY_DIR:?DEPLOY_DIR is not set}"/old_release
# Exits with error message if DEPLOY_DIR is unset or empty

Pattern 2: Explicit check

if [ -z "$DEPLOY_DIR" ]; then
  echo "ERROR: DEPLOY_DIR is not set" >&2
  exit 1
fi
rm -rf "$DEPLOY_DIR"/old_release

Pattern 3: set -u (nounset)

set -u
rm -rf "$DEPLOY_DIR"/old_release
# Exits if DEPLOY_DIR is unset (but not if empty!)

Pattern 4: Absolute path check

# Verify the path looks reasonable
case "$DEPLOY_DIR" in
  /home/deploy/*|/opt/app/*|/var/www/*)
    rm -rf "$DEPLOY_DIR"/old_release
    ;;
  *)
    echo "ERROR: Unexpected DEPLOY_DIR: $DEPLOY_DIR" >&2
    exit 1
    ;;
esac

Additional Safety Measures

  • Never use rm -rf / or rm -rf /* in scripts
  • Use --preserve-root (default in GNU rm)
  • Consider trash-cli instead of rm for recoverable deletion
  • Log what will be deleted before actually deleting:
    echo "Will delete: $DEPLOY_DIR/old_release"
    read -r -p "Continue? [y/N] " confirm
    [[ "$confirm" == "y" ]] && rm -rf "$DEPLOY_DIR"/old_release
    

Defense in Depth

The safest approach combines multiple guards:

set -euo pipefail
: "${DEPLOY_DIR:?Required}"
[[ "$DEPLOY_DIR" == /opt/app/* ]] || { echo "Bad path" >&2; exit 1; }
rm -rf "$DEPLOY_DIR"/old_release

Use Case

Deployment scripts, cleanup cron jobs, CI/CD pipelines, and any automation that deletes files based on variable paths. This is critical for preventing data loss in production environments.

Try It — Shell Script Linter

Open full tool