Lowercasing all directories under a directory
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!' {} +
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