From a5422c2b3f628f741bae18d531045bcaad60f440 Mon Sep 17 00:00:00 2001 From: first Date: Sun, 20 Jul 2025 19:45:59 +0000 Subject: [PATCH] Add rename_safe.sh --- rename_safe.sh | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 rename_safe.sh diff --git a/rename_safe.sh b/rename_safe.sh new file mode 100644 index 0000000..02a62bb --- /dev/null +++ b/rename_safe.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# Handle --dry-run flag +DRY_RUN=0 +if [ "$1" = "--dry-run" ]; then + DRY_RUN=1 + shift +fi + +# Directory to process (default: current directory) +DIR="." +if [ -n "$1" ]; then + DIR="$1" +fi + +# Function to process and rename a file +process_file() { + local original="$1" + local base ext modified sanitized_base + + # Skip directories and non-regular files + [ -f "$original" ] || return 0 + + # Split base and extension + if [[ "$original" == *.* ]]; then + ext=".${original##*.}" + base="${original%.*}" + else + base="$original" + ext="" + fi + + # Sanitize base only, not the extension + sanitized_base=$(echo "$base" | tr ' ' '_' | tr '[:upper:]' '[:lower:]') + sanitized_base=$(echo "$sanitized_base" | sed 's/[^a-zA-Z0-9._-]//g') + + # Truncate base to 250 chars to avoid hitting filesystem limits + base_truncated="${sanitized_base:0:250}" + + # Rebuild filename with sanitized base and original extension + modified="$base_truncated$ext" + + # Collapse multiple underscores + modified=$(echo "$modified" | tr -s '_') + + # Trim leading/trailing underscores + modified=$(echo "$modified" | sed 's/^_//' | sed 's/_$//') + + # Skip if empty + if [ -z "$modified" ]; then + echo "Error: Empty filename after sanitizing '$original'" >&2 + return 1 + fi + + # Skip if no change + if [ "$original" = "$modified" ]; then + echo "No changes needed: '$original'" + return 0 + fi + + # Collision handling + counter=1 + original_modified="$modified" + while [ -e "$modified" ]; do + modified="${base_truncated}_${counter}${ext}" + # Truncate again to avoid going over 255 chars + modified="${modified:0:255}" + ((counter++)) + done + + # Dry run + if [ "$DRY_RUN" -eq 1 ]; then + echo "Dry-run: Would rename '$original' → '$modified'" + return 0 + fi + + # Rename file + if mv -- "$original" "$modified"; then + echo "Renamed: '$original' → '$modified'" + else + echo "Error: Failed to rename '$original'" >&2 + return 1 + fi +} + +# Enter target directory +cd "$DIR" || { echo "Error: Failed to change directory to '$DIR'" >&2; exit 1; } + +# Process all non-hidden files +for file in *; do + process_file "$file" +done + +# Optional: process hidden files (uncomment below) +# for file in .??*; do +# process_file "$file" +# done \ No newline at end of file