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. 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

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.

14
  • 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
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, 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 .