Created
May 21, 2025 08:41
-
-
Save kwart/f6798c4bae5147439d4d2c2b34f918df to your computer and use it in GitHub Desktop.
Sanitize file/folder names
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
| #!/bin/bash | |
| # Requires: python3 and unidecode module | |
| # Install with: sudo apt install python3-unidecode OR pip3 install unidecode | |
| dry_run=false | |
| to_lowercase=false | |
| strip_parens=false | |
| print_usage() { | |
| echo "Usage: $0 [--dry-run] [--lowercase] [--strip-parens] <file-or-directory>" | |
| echo | |
| echo "Options:" | |
| echo " --dry-run Show renames without changing anything" | |
| echo " --lowercase Convert names to lowercase" | |
| echo " --strip-parens Remove parentheses and their contents from names" | |
| } | |
| rename_item() { | |
| local f="$1" | |
| local dir | |
| dir=$(dirname "$f") | |
| local base | |
| base=$(basename "$f") | |
| # Safe ASCII transliteration via Python | |
| local ascii_base | |
| ascii_base=$(python3 -c "import sys; from unidecode import unidecode; print(unidecode(sys.argv[1]))" "$base") | |
| # Optional lowercase | |
| if $to_lowercase; then | |
| ascii_base=$(echo "$ascii_base" | tr '[:upper:]' '[:lower:]') | |
| fi | |
| # Optional parentheses stripping | |
| if $strip_parens; then | |
| ascii_base=$(echo "$ascii_base" | sed -E 's/ *\([^)]*\)//g' | sed -E 's/ +/ /g' | sed -E 's/^ *| *$//g') | |
| fi | |
| if [[ "$base" != "$ascii_base" ]]; then | |
| local new_path="$dir/$ascii_base" | |
| echo "Renaming: $f -> $new_path" | |
| if ! $dry_run; then | |
| mv -n "$f" "$new_path" | |
| fi | |
| fi | |
| } | |
| rename_to_ascii() { | |
| local path="$1" | |
| local items=() | |
| if [ -d "$path" ]; then | |
| # Collect paths first | |
| while IFS= read -r f; do | |
| items+=("$f") | |
| done < <(find "$path" -depth) | |
| # Rename in the order returned by `find -depth` | |
| for item in "${items[@]}"; do | |
| rename_item "$item" | |
| done | |
| elif [ -f "$path" ]; then | |
| rename_item "$path" | |
| else | |
| echo "Error: '$path' is not a valid file or directory" | |
| exit 1 | |
| fi | |
| } | |
| # --- Parse flags --- | |
| while [[ "$1" == --* ]]; do | |
| case "$1" in | |
| --dry-run) dry_run=true ;; | |
| --lowercase) to_lowercase=true ;; | |
| --strip-parens) strip_parens=true ;; | |
| --help) print_usage; exit 0 ;; | |
| *) echo "Unknown option: $1"; print_usage; exit 1 ;; | |
| esac | |
| shift | |
| done | |
| # --- Path argument required --- | |
| if [ -z "$1" ]; then | |
| print_usage | |
| exit 1 | |
| fi | |
| rename_to_ascii "$1" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment