The point is obvious. There is more than one way to burn a book. And the world is full of people running about with lit matches. Every minority ... feels it has the will, the right, the duty to douse the kerosene, light the fuse. Every dimwit editor who sees himself as the source of all dreary blanc-mange plain porridge unleavened literature, licks his guillotine and eyes the neck of any author who dares to speak above a whisper or write above a nursery rhyme.”
~Ray Bradbury

Google+ Pinterest LinkedIn Tumblr +

Unlike Windows, Linux is case sensitive.  When you copy over files from a Windows machine and start working with a couple hundred of them, it can be infuriating that files are skipped during processing because the suffix was .JPG or .DocX when everything else is lower case.

As I’m learning Python, I put the following together.  Any improvements / optimizations are welcome of course.

# This script written on Linux Mint 17.3 with Python 3.4

import sys
import os
import fnmatch
from pathlib import PurePath as ppath
import glob #handles wildcards in listings better
import subprocess
import multiprocessing as mp

action = ""
workinglist = []

mplist = mp.Queue()
mpcorelimit = 0 #limit number of processors used
# 0=use all, or 2,4,6,8,10,12 . Consider drive I/O, 8-12 generally okay with SSD's

#-----------------------------------  Don't recommend changes beneath this line

def errout(problem):

def showhelp():
    print(' - filename to lower case (not foldernames, just files)')
    print("* Enclosing filenameorfilepattern in quotes is important")
    # because without quotes, python interprets every *.doc filename in current folder as a parameter
    print('Usage : [ r ] "filenameorfilepattern"')
    print("    r|R - recurse current dir and down")
    print('Example : "FileNumber?.JPG"')
    print('   r "*.DOCX"')
    print('   r "*"')
    print("The way this script works, if you have a file called 'r' in current folder then rename it by hand...")

def parseparams():
    args = ""
    # position 0=self, 1=flags, 2=file descriptors

    if len(sys.argv) == 1 or sys.argv[1].lower() == "help":

    args = sys.argv
    args.pop(0)    # don't need position zero (scriptname)
    action = str(args.pop(0).lower())
    fullpath = os.path.abspath(action)

    if action.lower() != "r":
        blob = glob.glob(fullpath) #interprets wildcards & absolute names correctly
        for i in blob:

    else: #process recursively
        fullpath = os.path.abspath(str(args.pop(0).lower()))
        pattern = os.path.basename(fullpath)
        dirname = fullpath.rstrip(pattern)

        # for this you have to os.walk/enumerate all files, then find those that match filepattern
        for root, dirs, files in os.walk(os.path.abspath(dirname)):
            for filename in fnmatch.filter(files,pattern):
                returnlist.append(os.path.join(root, filename))

def loweritem(infile):
    print(".", end='')
    fnameis = os.path.basename(infile)
    pathis = infile.split(fnameis)[0]

    procresult = subprocess.os.rename(infile, pathis+fnameis.lower())

def tolower(workinglist):
    print("Renaming the following " + str(len(workinglist)) + " files:")
    for i in workinglist:
        print("    " + str(i))

        input("Hit ENTER to continue or CNTL-C to abort..")

    print("Working", end='')

    #define no. of CPU cores to be used for processing
    # assumes multithreading, thus the *2
    pool = mp.Pool(processes=mp.cpu_count() * 2)
    if not mpcorelimit == 0 and pool > mpcorelimit:
        pool = mpcorelimit

    pool_outputs =, workinglist)
    pool.close() # no more tasks left
    pool.join() # wrap up tasks and join

    for i in pool_outputs:
        if not i == "None":
            goodrun = False
    if goodrun:
        print("*    ---- errors detected")
        for i in pool_outputs:

action, workinglist = parseparams()

if tolower(workinglist): #if processing returns True
    print("Task completed.")
else: #returned False
    errout("problem with renaming detected, script halted")

Leave A Reply

Secured By miniOrange