Note that (beside the unrelated and very limited one in util-linux) there are now a few variants of the perl rename command (originally a very short example script that came with perl 3.0).
With the one from https://metacpan.org/dist/File-Rename and found in the rename package on recent Debian-based systems, to turn to uppercase all ASCII letters in the name of an arbitrary file, but not in the extension if any, you'd do:
rename -d -- 's/.*\.|.*/\U$&/s' "$file"
Where:
-d makes sure the dirname of the file is not affected.
-- is needed with that implementation as otherwise a $file starting with - would be taken as an option (with disastrous consequences for a file called --e=exec("rm -rf ~") for instance).
- the
s flag to the substitute is needed as otherwise . would not match on a newline character (a character that is as valid as any in a file name).
.*\.|.* matches either anything up to the rightmost . (as .* is greedy) or everything, bearing in mind that perl tries the left pattern first and contrary to POSIX regexps, doesn't try to see if the other one may yield a longer match. So it would match up to before the extension for files with an extension, and the whole filename for those without.
$& is what was matched. Prefixed with \U, that's converted to uppercase like in ex/vi where perl borrowed it from.
Examples with -n (for dry-run):
$ rename -n -d -- 's/.*\.|.*/\U$&/s' $'--e=system("echo reboot") #/foo\nbar.jpg'
rename(--e=system("echo reboot") #/foo
bar.jpg, --e=system("echo reboot") #/FOO
BAR.jpg)
$ rename -n -d -- 's/.*\.|.*/\U$&/s' .config/gtkrc
rename(.config/gtkrc, .config/GTKRC)
$ rename -n -d -- 's/.*\.|.*/\U$&/s' .bashrc
$
(in .bashrc here, bashrc is considered as an extension).
Now, one problem is that it only works on ASCII file names.
It would convert a UTF-8 Stéphane.jpg to STéPHANE.jpg instead of STÉPHANE.jpg for instance (at least where that é is in precomposed form) or worse convert a BIG5-HKSCS Stéphane.jpg to St<U+F310>phane.jpg.
Working around that with rename is a bit of a pain. You need something like:
rename -n -d -- '
use Encode::Locale;
use Encode;
$_ = decode(locale_fs => $_);
s/.*\.|.*/\U$&/s;
$_ = encode(locale_fs => $_)' "$file"
To consider the files as being encoded in the locale's charset.
Beware however that it would turn bytes that can't be decoded into a character into some replacement character (something like ? or � depending on the locale; in the C locale, that includes all byte values above 127).
In the end, it may be easier and safer to do it with your shell's operators. Like in zsh:
case $file:t in
(*.*) newfile=$file:h/$file:t:r:u.$file:e;;
(*) newfile=$file:h/$file:t:u;;
esac
[[ $file = $newfile ]] || mv -i -- $file $newfile
(where :t, :h, :r, :e, :u are csh-style modifiers that yield respectively the tail, head, rootname, extension, and turn to uppercase).
zsh's operators will do their best effort: convert characters that can be decoded and leave alone the sequences of bytes that can't be decoded.
zsh also has its own builtin batch renaming tool: zmv, implemented as an autoloadable function:
autoload -Uz zmv # best in ~/.zshrc
zmv '(*).(*)' '$1:u.$2'
(to rename only the non-hidden files that do have an extension)
zmv also does a few sanity checks before starting the renaming which also makes it safer than rename.