I am a newbie and I have searched everywhere for this, I have also tried combining a lowercase renaming command with some regex to get Title Case instead of lowercase but I wasn't very successful.

This command converts everything (files + folders) inside the given folder to lowercase:

while IFS= read -r -d '' file; do mv -b -- "$file" "${file,,}"; done < <(find . -depth -name '*[A-Z]*' -print0)

And this is my attempt at title case, it works, but it is not recursive:

find . -name "*.flac" -print0 | while read -d $'\0' file; do rename 's/(^|[\s\(\)\[\]_-])([a-z])/$1\u$2/g' *; done

Those are just some of my attempts, if there are better, shorter solutions I would pretty much like those instead.

Could you please help me out? Thanks in advance!

EDIT: I forgot to mention, my files look like this: "09 - the Road to Home - Amy MacDonald.flac"; should be renamed to "09 - The Road To Home - Amy Macdonald.flac". Notice how there are already title cased words as well as uppercase letters in the middle of a word.

  • Only bash, oneliners? could be easily done with python. Feb 24, 2015 at 13:12
  • 1
    @JacobVlijm Go for it, then ;-)
    – Jos
    Feb 24, 2015 at 13:16
  • I am not very familiar with python. Sorry :(
    – BlackR
    Feb 24, 2015 at 13:51

2 Answers 2


To use the script below, you do not need more than the ability to paste :)

How to use

  1. Paste the script below into an empty file, save it as (e.g.) rename_title.py
  2. make it executable (for convenience reasons) chmod u+x rename_title.py
  3. Run it with the directory to rename as argument:

    /path/to/rename_title.py <directory/to/rename>

The script

#!/usr/bin/env python3
import os
import sys
import shutil

directory = sys.argv[1]

skip = ["a", "an", "the", "and", "but", "or", "nor", "at", "by", "for", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "to", "up", "with", "as"]
replace = [["(", "["], [")", "]"], ["{", "["], ["}", "]"]]
def exclude_words(name):
    for item in skip:
        name = name.replace(" "+item.title()+" ", " "+item.lower()+" ")
    # on request of OP, added a replace option for parethesis etc.
    for item in replace:
        name = name.replace(item[0], item[1])
    return name

for root, dirs, files in os.walk(directory):
    for f in files:
        split = f.find(".")
        if split not in (0, -1):
            name = ("").join((f[:split].lower().title(), f[split:].lower()))
            name = f.lower().title()
        name = exclude_words(name)
        shutil.move(root+"/"+f, root+"/"+name)
for root, dirs, files in os.walk(directory):
    for dr in dirs:
        name = dr.lower().title()
        name = exclude_words(name)
        shutil.move(root+"/"+dr, root+"/"+name)


a file > A File
a fiLE.tXT > A File.txt
A folder > A Folder
a folder > A Folder

And more complex, excluding ["a", "an", "the", "and", "but", "or", "nor", "at", "by", "for", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "to", "up", "with", "as"]:

down BY the rIVER for my uncLE To get water.TXT


Down By the River for My Uncle to Get Water.txt

etc, it simply makes all files and folders (recursively) Title Case, extensions lower case.

EDIT: I have added all articles, conjunctions and prepositions that do not need to be capitalized according to the capitalization rules for song titles.

  • Works like a charm, could you also add a way to skip the title case command after converting everything to lowercase for some words like to, the, in, as, for (but not if at beginning of filename)?
    – BlackR
    Feb 24, 2015 at 14:41
  • Your assumption is correct. I do not know yet but I think I can figure a way to add those myself to your code if needed. If you can do it for some words I can use your template to add more.
    – BlackR
    Feb 24, 2015 at 14:51
  • @BlackR should be pretty perfect now. Actually the result of an interesting question. It was much more complicated than I anticipated, but definitely challenging and very usable in a wider range of situations, since the rules you practice are simply proper naming rules. Thanks for the question! Feb 24, 2015 at 15:54
  • Could you help me with adding some new functionality to your program? I would like to replace all parenthesis with brackets: ( becomes [ and ) becomes ], same for { and }. And I would also like to keep some words uppercase (TV, OP and ED; II, III, IV, V, VI ... IX - roman letters). Thanks in advance!
    – BlackR
    Mar 10, 2015 at 17:12
  • @BlackR sure, I need to referesh my memory a bit though :) Shall I make an edit, referring to this comment? (probably not today). Mar 10, 2015 at 17:38

If you use find's -exedir, then the names get passed any command with the leading components of the pathname removed, like ./sOmE fILE. You could then title-case each sequence of word-characters that is preceded by either the leading / or by whitespace, e.g.

find path/ -execdir rename -nv -- 's/(?<=[\/\s])(\w)(\w*)/\u$1\L$2/g' {} +
  • Well, your command works only for the first letter. My filenames also contain blank spaces. Here is an example: 09 - the Road to Home - Amy MacDonald.flac; should be converted to 09 - The Road To Home - Amy Macdonald.flac. Please observe that there can be uppercase letters in the middle of a word as well as words already in title case.
    – BlackR
    Feb 24, 2015 at 13:51
  • Oops you are right of course - please see the revised version. I'm aware it doesn't exclude conjunctions (in, to etc.) as requested via your comments. Feb 24, 2015 at 18:17
  • Nevertheless, it's useful info. You could use something like this perhaps: "rename 's/\b((?!(a|of|that|to)\b)[a-z]+)/\u$1/g' *". I think there is still the issue with skipping if at beginning of file name.
    – BlackR
    Feb 24, 2015 at 18:23

You must log in to answer this question.

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