11

I'd like to recursively rename all files and folders (sub-folders) to uppercase.

I found some scripts that will do it to lowercase, but I don't know how to change them so it will do it the other way around (lower to upper).

The script that I found and works for lowercase, but I didn't knew how to modify is:

rename 'y/A-Z/a-z/' *

It's from man rename.

  • Without knowing anything about the rename command, I suspect that if you try rename 'y/a-z/A-Z/' * you will get what you want. Careful where you test it through. – Warwick Jul 7 '14 at 7:17
  • it's not recursevely (because the initial one wasn't ...) but it works. I can't understand why I didn't tought at that miself :), thenks! – jnhghy - Alexandru Jantea Jul 7 '14 at 7:19
  • I think that given the quality of Gilles' answer, that it woud be good if you accept his answer in preference to mine. It is much more complete than mine and explains your options (and how it all works). Thanks. – Warwick Jul 9 '14 at 0:11
8

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.

| improve this answer | |
5

Stolen (with a minor edit) from Gilles post here

find <DIR> -depth -type d -exec rename -n 's!/([^/]*/?)$!\U/$1!' {} +

| improve this answer | |
  • This doesn't act recursively. – Gilles 'SO- stop being evil' Jul 7 '14 at 21:42
  • True. I should have read the question properly. I don't have access to the rename command, but I believe that this will work recursively - find <dir> -exec rename 'y/a-z/A-Z/' {} \; – Warwick Jul 7 '14 at 23:03
  • It's a bit more complicated than that, because you're renaming directories while find is recursing into them. – Gilles 'SO- stop being evil' Jul 7 '14 at 23:09
  • @Gilles - OK. After doing a bit of research, it appears that I need to add the -depth option to find so that directory names are handled last. So find <dir> -depth -exec rename 'y/a-z/A-Z/' {} \; should handle the renaming directories issue. Correct? – Warwick Jul 7 '14 at 23:41
  • No, that still doesn't work, because rename changes e.g. foo/bar to FOO/BAR and FOO doesn't exist at that point. – Gilles 'SO- stop being evil' Jul 7 '14 at 23:45
2

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.

| improve this answer | |
1

find -execdir | rename

This would be the best way to do it if it weren't for the relative path madness, as it avoids Perl regex fu to only act on the basename:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find a -depth -execdir rename 's/(.*)/\U$1/' '{}' \;

-execdir first cds into the directory before executing only on the basename.

Unfortunately, I can't get rid of that PATH hacking part, find -execdir refuses to do anything if you have a relative path in PATH...: https://askubuntu.com/questions/621132/why-using-the-execdir-action-is-insecure-for-directory-which-is-in-the-path/1109378#1109378

| improve this answer | |
0

Try this after moving to directory where you would like to rename the files:

for word in `ls -ltr |tail -n +2 |awk '{print $9}'`
do
  a=$(echo $word | tr '[a-z]' '[A-Z]')
  mv $word $a
  echo "Done Successfully"
done
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.