How can I use rename to recursively rename everyting to uppercase
Note that you're using the Perl script called rename
distributed by Debian and derivatives (Ubuntu, Mint, …). Other Linux distributions ship a completely different, and considerably less useful, command called rename
.
y/A-Z/a-z/
translates each character in the range A
through Z
into the corresponding character in the range a
through z
, i.e. ASCII uppercase letters to the corresponding lowercase letter. To perform the opposite translation, use y/a-z/A-Z/
. Another way to write the same command is rename '$_ = uc($_)' *
— uc
is the uppercase function, and the rename
command renames files based on the transformation made to the $_
variable.
rename '…' *
only renames files in the current directory, because that's what *
matches. Dot files (files whose name begins with .
) are skipped, too.
If you want to rename files in the current directory and in subdirectories recursively, you can use the find
command to traverse the current directory recursively. There is a difficulty here: if you call rename
, this renames both the directory and the base name part. If you call rename
on a directory before recursing into it (find -exec rename … {} \;
), find
gets confused because it's found a directory but that directory no longer exists by the time it tries to descend into it. You can work around this by telling find
to traverse a directory before acting on it, but then you end up attempting to rename foo/bar
to FOO/BAR
but the directory FOO
doesn't exist.
A simple way to avoid this difficulty is to make the renaming command act only on the base name part of the path. The regular expression ([^/]*\Z)
matches the final part of the path that doesn't contain a /
.
find . -depth -exec rename 's!([^/]*\Z)!uc($1)!e' {} +
The shell zsh provides more convenient features for renaming — even more cryptic than Perl, but terser and often easier to compose.
The function zmv
renames files based on patterns. Run autoload -U zmv
once to activate it (put this line in your .zshrc
).
In the first argument to zmv
(the pattern to replace), you can use zsh's powerful wildcard patterns. In the second argument to zmv
(the replacement text), you can use its parameter expansion features, including history modifiers.
zmv -w '**/*' '$1$2:u'
Explanation:
-w
— automatic assign numeric variables to each wildcard pattern**/*
— all files in subdirectories, recursively (**/
matches 0, 1 or more levels of subdirectories)$1
— the first numeric variable, here matching the directory part of each path$2:u
— the second numeric variable, here matching the base name part of each path, with the:u
modifier to convert the value to uppercase
As an added bonus, this respects the ambient locale settings.
If you aren't sure about a zmv
command you wrote, you can pass the -n
option to print what the command would do and not change anything. Check the output, and if it does what you want, re-run the command without -n
to actually act.
Stolen (with a minor edit) from Gilles post here
find <DIR> -depth -type d -exec rename -n 's!/([^/]*/?)$!\U/$1!' {} +
I'd like to direct anyone who's still being linked to this answer to the excellent answer Guiles Quernot gave to this question which doesn't require find
.
The resulting command would be:
shopt -s globstar
rename -n 'y/a-z/A-Z/' **
But before running please read the answer linked for caveats regarding old bash versions.
Finally in case someone is wondering what does the y///
command does in perl regex
. Here's a link to the relevant documentation.