Lowercasing all directories under a directory

I want to lowercase every directories’ name under a directory. With which commands can I do that?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

All the directories at one level, or recursively?

Zsh

At one level:

autoload zmv
zmv -o-i -Q 'root/(*)(/)' 'root/${1:l}'

Recursively:

zmv -o-i -Q 'root/(**/)(*)(/)' 'root/$1${2:l}'

Explanations: zmv renames files matching a pattern according to the given replacement text. -o-i passes the -i option to each mv command under the hood (see below). In the replacement text, $1, $2, etc, are the successive parenthesized groups in the pattern. ** means all (sub)*directories, recursively. The final (/) is not a parenthesized group but a glob qualifier meaning to match only directories. ${2:l} converts $2 to lowercase.

Portable

At one level:

for x in root/*/; do mv -i "$x" "$(printf %s "$x" | tr '[:upper:]' '[:lower:]')"; done

The final / restricts the matching to directories, and mv -i makes it ask for confirmation in case of a collision. Remove the -i to overwrite in case of a collision, and use yes n | for …. to not be prompted and not perform any renaming that would collide.

Recursively:

find root/* -depth -type d -exec sh -c '
    t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
    [ "$t" = "$0" ] || mv -i "$0" "$t"
' {} ;

The use of -depth ensures that deeply nested directories are processed before their ancestors. The name processing relies on there being a /; if you want to call operate in the current directory, use ./* (adapting the shell script to cope with . or * is left as an exercise for the reader).

Perl rename

Here I use the Perl rename script that Debian and Ubuntu ship as /usr/bin/prename (typically available as rename as well). At one level:

rename 's!/([^/]*/?)$!L/$1!' root/*/

Recursively, with bash ≥4 or zsh:

shopt -s globstar  # only in bash
rename 's!/([^/]*/?)$!L/$1!' root/**/*/

Recursively, portably:

find root -depth -type d -exec rename -n 's!/([^/]*/?)$!L/$1!' {} +

Method 2

There isn’t a single command that will do that, but you can do something like this:

for fd in */; do
  #get lower case version
  fd_lower=$(printf %s "$fd" | tr A-Z a-z)
  #if it wasn't already lowercase, move it.
  [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"
done

If you need it to be robust, you should account for when there is already two directories that differ only in case.

As a one-liner:

for fd in */; do fd_lower=$(printf %s "$fd" | tr A-Z a-z) && [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"; done

Method 3

I took this as a one-liner challenge 🙂 First, establish a test case:

$ for d in foo Bar eVe; do mkdir -p dcthis/$d; touch dcthis/a${d}.txt; done
$ ls dcthis/
Bar     aBar.txt    aeVe.txt    afoo.txt    eVe     foo

I use find to spot the directories with uppercase letters and then downcase them via sh -c 'mv {} echo {} | tr [:upper:] [:lower:]'. Guess using sh -c is a bit hack-ish, but my head always explodes when I try escaping things for find directly.

$ (cd dcthis && find . -maxdepth 1 -type d -path '*[A-Z]*' -exec sh -c 'mv {} `echo {} | tr [:upper:] [:lower:]`' ;)
$ ls dcthis/
aBar.txt    aeVe.txt    afoo.txt    bar     eve     foo

Be warned: This solution does not check whether downcasing leads to collisions!

Method 4

find -execdir rename

This renames files and directories with a regular expression affecting only basenames.

So for a prefix you could do:

PATH=/usr/bin find . -depth -execdir rename 's/(.*)/L$1/' '{}' ;

or to affect files only:

PATH=/usr/bin find . -type f -execdir rename 's/(.*)/L$1/' '{}' ;

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

I have explained it in more detail at: https://stackoverflow.com/questions/16541582/find-multiple-files-and-rename-them-in-linux/54163971#54163971


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x