Rinominare file ricorsivamente in Linux senza rompere nomi e percorsi

Rinominare molti file in sicurezza con Bash, find, parameter expansion e rename: dry-run, spazi e newline, collisioni, util-linux vs Perl rename e procedure reversibili.

Rinominare cento file è semplice. Rinominare ricorsivamente centomila file senza distruggere nomi contenenti spazi, newline o caratteri strani richiede qualche attenzione in più.

Il vecchio one-liner find ... | rename ... può funzionare in alcuni ambienti, ma l’output testuale di find non è un formato robusto per pathname arbitrari e il comando rename non è uguale su tutte le distribuzioni.

Prima regola: simulare

find . -type f -name '*.mp3' -print0 |
while IFS= read -r -d '' file; do
    dir=${file%/*}
    name=${file##*/}
    new=${name//pippo/pluto}

    printf '%q -> %q\n' "$file" "$dir/$new"
done

-print0 separa i risultati con NUL, quindi non si rompe su spazi, tab o newline nei nomi.

Rinominare con Bash parameter expansion

find . -type f -name '*pippo*.mp3' -print0 |
while IFS= read -r -d '' file; do
    dir=${file%/*}
    name=${file##*/}
    new=${name//pippo/pluto}
    target="$dir/$new"

    [[ "$file" == "$target" ]] && continue

    if [[ -e "$target" ]]; then
        printf 'SKIP collisione: %q -> %q\n' "$file" "$target" >&2
        continue
    fi

    mv -- "$file" "$target"
done

-- chiude le opzioni di mv, quindi un nome che inizia con - non viene interpretato come parametro.

Non usare for file in $(find …)

for file in $(find . -type f); do
    ...
done

Questa costruzione divide l’output secondo whitespace e globbing della shell. Un file chiamato mix estate 2026.mp3 smette di essere trattato come un unico pathname.

Il problema dei due rename

Su Linux sono molto diffuse almeno due famiglie chiamate rename: util-linux rename e Perl rename. La sintassi non è intercambiabile.

rename --version
type -a rename
man rename

util-linux rename

# dry-run
rename --no-act pippo pluto ./*pippo*.mp3

# esecuzione senza sovrascrivere target esistenti
rename --no-overwrite pippo pluto ./*pippo*.mp3

La man page corrente avverte che, senza --no-act, --interactive o --no-overwrite, non ci sono salvaguardie contro rinominazioni drastiche.

Perl rename

# sintassi tipica della variante Perl
rename -n 's/pippo/pluto/g' ./*pippo*.mp3

# dopo aver controllato il dry-run
rename 's/pippo/pluto/g' ./*pippo*.mp3

Cambiare estensione

find . -type f -name '*.jpeg' -print0 |
while IFS= read -r -d '' file; do
    target="${file%.jpeg}.jpg"

    [[ -e "$target" ]] && {
        printf 'SKIP: %q esiste già\n' "$target" >&2
        continue
    }

    mv -- "$file" "$target"
done

Normalizzare spazi

find . -depth -name '* *' -print0 |
while IFS= read -r -d '' path; do
    dir=${path%/*}
    name=${path##*/}
    new=${name// /_}
    target="$dir/$new"

    printf '%q -> %q\n' "$path" "$target"
done

-depth è utile se rinominiamo anche directory, perché visita prima i figli e poi il contenitore.

Case-only rename

Su filesystem case-sensitive File.txt e file.txt sono distinti. Su filesystem case-insensitive può essere necessario passare attraverso un nome temporaneo per cambiare soltanto maiuscole/minuscole.

Rollback

Per una rinominazione importante generiamo prima una mappa vecchio→nuovo. Un rename è facilmente reversibile soltanto se sappiamo con certezza quale fosse il nome originale.

printf '%q\t%q\n' "$file" "$target" >> rename-map.tsv

Regola pratica

  • prima dry-run;
  • tratta i pathname come dati, non come parole;
  • usa NUL come delimitatore tra processi;
  • controlla collisioni;
  • verifica quale rename hai installato;
  • mantieni una mappa per le operazioni importanti.

Riferimento

util-linux rename(1).