I need to elaborate a little bit more. These commands are actually for a BBS FTN tosser. The configuration file for which these commands reside in, only allow specific declarations. Here are a few examples:

exec "/home/imp/imp/poll.sh" *.su? *.mo? *.tu? *.we? *th? *.fr? *.sa? *.pkt
flag toss!.now [0-9a-Z][0-9a-Z][0-9a-Z][0-9a-Z][0-9a-Z][0-9a-Z][0-9a-Z][0-9a-Z].???
exec "/home/imp/imp/poll.sh /home/imp/hpt/secure" /home/imp/hpt/secure/*.[STFWMstfWM][ouaherOUAHER][0-9A-ZA-a] *.[pP][kK][tT]

So that's why I want to use that syntax ([0-9a-z] (for example)). The problem is that the mailer is looking for lowercase filenames, but only uppercase filenames exist.

I'm trying to convert a file from uppercase to lowercase using the syntax below:

mv /home/imp/hpt/outbound/[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z].[STFWMfWM][OUAHER][0-9] /home/imp/hpt/outbound/[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z].[stfwm][ouaher][0-9a-za-a]

I don't think I have the syntax correct.

Here's an example of a file name I want to rename:


Any and all help is greatly appreciated. Thanks.

  • In Bash 4: "${file,,}". See the always excellent Wooledge Wiki...
    – jasonwryan
    Jul 23, 2016 at 3:26
  • Thanks for that.. but i'd like to use the [x-xx-x].. format
    – ignatius
    Jul 23, 2016 at 3:29
  • 1
    You might be thinking of our friend tr, then...
    – Jeff Schaller
    Jul 23, 2016 at 3:56
  • Sigh The Wooledge wiki describes exactly how to do that...
    – jasonwryan
    Jul 23, 2016 at 4:06
  • You can't use [a-Z] as a range! It needs to be [a-zA-Z].
    – Kusalananda
    Jul 23, 2016 at 6:48

7 Answers 7


Whenever you want to change one class of characters into another, use tr.

for f in *; do
    test -f "$f" && echo mv "$f" "$( tr '[:upper:]' '[:lower:]' <<<"$f" )"

The script will rename all files in the current directory to all lowercase letters. It will skip directories. Remove the echo when you're certain it does what you want. You may replace [:upper:] and [:lower:] with A-Z and a-z respectively if you only have standard ASCII filenames (note: A-Z, not [A-Z]).

Alternatively, using Bash's built-in upper-to-lowercase variable substitution:

for f in *; do
    test -f "$f" && echo mv "$f" "${f,,}"
  • Hi, can you explain what the <<< does, please?
    – Daniele
    Dec 17, 2018 at 16:30
  • 1
    @Daniele It's a "here-string". It passes the given string to the standard input of the command. tr '[:upper:]' '[:lower:]' <<<"HELLO" would output hello. It is similar to doing echo 'HELLO' | tr '[:upper:]' '[:lower:]', but without involving echo. Here-strings are an extension to the standard shell syntax, but they are understood by bash.
    – Kusalananda
    Dec 17, 2018 at 16:39
  • Two questions: (a) what is the role of the test -f "$f" ? and (b) why wasn’t this answer accepted?
    – Manngo
    Sep 18, 2021 at 3:21
  • @Manngo a) The test makes sure that $f refers to a regular file, and not, say, a directory. b) Nobody knows.
    – Kusalananda
    Sep 18, 2021 at 5:34
  • (a) Thanks; (b) Oh well, have an upvote anyway.
    – Manngo
    Sep 18, 2021 at 7:35

This answer from Alex B at Stack Overflow will help you to rename both files and subfolders under a directory.

It consists on using both find and rename Linux commands. I would only add that the command provided in the link above will modify directory names as well. In case that you want only to change file names, you need to change the -depth option with the next one: -type f.

Another regular expression given to the rename command could be the one below, which I think it is more easy to understand.

 find my_root_dir -type f -execdir rename 'y/A-Z/a-z/' {} \;

Be aware that there is a necessary white space between the rename command regular expression and the curly braces, and another one between them and the command termination character \;

Also remember that it will rename every file not only in the directory where you execute the command from (the my_root_dir directory) but also to all the files under every subfolder contained into this one. You can use the -maxdepth 0 option with the find command to force it to only apply the tests and actions to the starting-points themselves.

  • 2
    note: this is the perl rename, not the util-linux rename.
    – Jasen
    Jul 23, 2016 at 4:15
  • 1
    I'm not certain that find is needed.. rename 'y/A-Z/a-z/' *[A-Z]*
    – Jasen
    Jul 23, 2016 at 4:18
  • None of these commands actually work. I keep getting the rename "help screen" :/
    – ignatius
    Jul 23, 2016 at 5:04
  • Sorry, I made a mistake with the regular expression in the second command. I also modified the find command -execdir option so it now runs the required action on every directory where a file is found. I tested it and worked. Could you please let me know if it works for you now?
    – gongomgra
    Jul 23, 2016 at 7:09
  • Thank you, I much prefer this. I used ls -d * | xargs --delimiter '\n' --max-args=1 rename 'y/A-Z/a-z/' Jul 14, 2020 at 2:41

With zsh:

autoload zmv # best in ~/.zshrc
zmv '[[:alnum:]](#c8).[STFWMfWM][OUAHER][0-9]' '${(L)f}'

That will take of checking for potential conflicts.


Here you are:

for i in $( ls | grep [A-Z] ); do mv -f $i `echo $i | tr 'A-Z' 'a-z'`; done

And this is an example showing how it works:

X$ ls

123123HHHSK FILE_[1-10] DB
FILE1       FILE_{1-10} SQL

X$ for i in $( ls | grep [A-Z] ); do mv -f $i `echo $i | tr 'A-Z' 'a-z'`; done

X$ ls

123123hhhsk file_[1-10] db
file1       file_{1-10} sql 

EDIT: We could easily use -f option, to avoid using yes y.

  • 1
    $( ls | grep [A-Z] ) should be the same as the glob *[A-Z]*. If it's not, set LANG=C and try it. Jul 23, 2016 at 12:14

Simple script rename-lowerfor current dir, if rename command is not working you can change in mv.

# script to replace all upper case to lower case
for i in *
        d=$(echo ${i} |tr [:upper:] [:lower:]);
        if [ ${d} != ${i} ]
                echo "renaming:" ${i} ${d}
                rename ${i} ${d} ${i}
#               mv ${i} ${d}


$ ls
tmp.EwcM2s6kfU  tmp.kcUX6rtjiu  tmp.kzpqLvWcnQ  tmp.N1H8bLt1at

$ rename-lower
renaming: tmp.EwcM2s6kfU  tmp.ewcm2s6kfu
renaming: tmp.kcUX6rtjiu  tmp.kcux6rtjiu
renaming: tmp.kzpqLvWcnQ  tmp.kzpqlvwcnq
renaming: tmp.N1H8bLt1at  tmp.n1h8blt1at

Your mv command doesn't work as intended because what happens is that the shell expands the wildcards and the result is a single list of file names which is passed to mv. The syntax of mv is mv SOURCE1 SOURCE2 … DESTINATION, i.e. all the arguments except the last one are file names to be moved to the destination. mv does not support pattern-based renaming.

If you have the prename utility, you can use it to rename files by running Perl code on each name. The syntax is prename PERL-CODE FILENAME1 FILENAME2 …. The file name is in the variable $_, and the function lc converts its argument into lowerecase, so you can use the following code:

prename '$_ = lc($_)' /home/imp/hpt/outbound/[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z].[STFWMfWM][OUAHER][0-9]

If you don't have this utility, you can download a version from CPAN.

Alternatively, you can do the renaming in the shell, with a loop. In bash, you can use ${x,,} to get the value of x converted to lowercase.

for x in /home/imp/hpt/outbound/[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z].[STFWMfWM][OUAHER][0-9]; do
  mv "$x" "${x,,}"

Following worked for me:

    for f in *; do mv -T "$f" "$(echo $f | tr [A-Z] [a-z])"; done

It would be interesting, what the problems are with my solution.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .