Renaming files in a directory tree
From IlugCal
This script may be used to rename files in a directory tree, by replacing white-spaces with hyphen and changing all uppercase characters to lowercase. It would generate a simple log file of the changes it has made, and also warns the user before accidental replacing of the files.
Usage: 1. Copy the code below, and save it in a file an name it renamer.py 2. Open a terminal and type in python renamer.py /nameOfDirectory, where nameOfDirectory can be an absolute path or a directory under your current working directory.
#!/usr/bin/python
import sys, os, re, time
def gettime():
timestamp = time.localtime(time.time())
return time.asctime(timestamp)
def listdir(dirPath):
fileshere = os.listdir(dirPath)
for name in fileshere:
path_old = os.path.join(dirPath, name)
name_low = name.lower()
name_low_hyp = name_low.replace(" ","-")
if name_low_hyp in fileshere and name_low_hyp!= name:
print name_low_hyp + " already exists. Replace it? "
ans = raw_input(" Remove existing file?[y|n] ")
if ans == 'n':
name_low_hyp = '~' + name_low_hyp
path_new = os.path.join(dirPath, name_low_hyp)
os.rename(path_old, path_new)
f.write(gettime() + "\t "+ name + " ----> " + name_low_hyp + "\n")
if os.path.isdir(path_new):
listdir(path_new)
f = open(r'remover.log', 'w')
f.write('****************** renamer.py log file ******************\n')
f.write('renamer.py started at ' + gettime() + '\n')
for name in sys.argv[1:]: listdir(name)
f.write('remover.py ended at ' + gettime())
Feel free to tune it for your needs.
Sayamindu Dasgupta's version, ... a bit edited
#!/usr/bin/env python
import os, sys, shutil
def move_stuff(root, name):
newname = name.lower()
if newname.count(" ") > 0:
newname = newname.replace(" ", "")
print "Renaming " + os.path.join(root, name) + " to " + os.path.join(root, newname)
shutil.move(os.path.join(root, name), os.path.join(root, newname))
if len(sys.argv) < 2:
print "Usage:\n" + sys.argv[0] + " directory"
raise SystemExit
top=sys.argv[1]
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
move_stuff(root, name)
for name in dirs:
move_stuff(root, name)
print "Done!!"
[edit] bash approach
Reply to http://list.ilug-cal.org/pipermail/ilug-cal-discuss/2007-August/001589.html
#!/bin/bash
# Better unset the variables first to avoid any problems
unset i filename list
# Read in the list of files recursively into an array
# Note that by changing the arguments to find, you can generate the kind of list you want
while read -d $'\0' filename
do
list[i++]="$filename"
done < <(find "$1" -print0)
# Actual processing
# We loop over the array in reverse because otherwise, renaming would mismatch the list in the array
# We will not rename the directory given as the argument
for (( i=$(( ${#list[@]} - 1 )) ; i > 0 ; i-- ))
do
# The following one liner actually does all the work
mv -v "${list[i]}" "${list[i]%/*}/$( echo "${list[i]##*/}" | tr ' [:upper:]' '-[:lower:]' )" 2>/dev/null
done
Invoke the script with the name of the parent directory as the argument. Make sure you quote the name/path if there are spaces or newlines. The script will NOT rename the directory provided as the argument.
There is a caveat in this particular `tr' approach that there will be an error if the filename is passed through without translation. Since this is mv, it can be safely ignored. With the stderr redirected, you won't even notice any errors. This'll need to be refined for useage with any other problem.
Anyway, this should server as a skeleton for recursive directory processing in bash. The while loop is important because it allows us to read in a directory structure with spaces and newlines in the filenames. Aftwards, there are loads of possibilities by looping through the array containing the list of files.
HTH.
Mrugesh
P.S. Btw, there are loads of possibilities for improving this script. One could tighten it up for signal handling or error processing etc. That applies for most of the bash scripts though, I guess.

