5

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.

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

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()))
        else:
            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)

Examples:

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

becomes:

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.

15
  • 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 '15 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 '15 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! – Jacob Vlijm Feb 24 '15 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 '15 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). – Jacob Vlijm Mar 10 '15 at 17:38
1

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' {} +
3
  • 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 '15 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. – steeldriver Feb 24 '15 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 '15 at 18:23

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.