Skip to content

Instantly share code, notes, and snippets.

@billspat
Created January 23, 2016 13:54
Show Gist options
  • Save billspat/9b57dc9a7643b63e87dd to your computer and use it in GitHub Desktop.
Save billspat/9b57dc9a7643b63e87dd to your computer and use it in GitHub Desktop.
make os x files windows friendly
# fix names
# toy program that looks for non-windows-friendly file names on an OS X system and emits "mv" commands to rename them.
# checks that files are uniquely named. Does not check for length of file path
# run the mv commands to make the changes
# TEST before using; no guarantees; in Public domain
import re
import os, sys
#
# illegal chars
# \ / : * ? " < > |
def fixpathname(pathname,replacechar = '_'):
"""given a path name (file or directory), replace all characters not whitelisted
with one replacement char, and make sure new name is unique so it won't clobber existing files
returns new name """
newfname = pathname
pattern = re.compile('[^-.\w\s\(\)!\#$\&\{\},\~\+]')
if (pattern.search(pathname)):
# replace non-matching characters
newfname = pattern.sub(replacechar ,pathname)
# replace duplicates of these
newfname = re.sub(replacechar*2, replacechar,newfname)
newfname = re.sub(replacechar+" ", replacechar,newfname)
return(newfname)
def makeunique(fname, fnames):
"""given a filename and list of other filenames, make sure fname is unique and if not add a number"""
suffix = 0
while fname in fnames:
suffix += 1
root,ext = os.path.splitext(fname)
fname = "{1}_{0}{2}".format(suffix, root, ext)
return(fname)
def printfixes(f):
"""given dict of old and new names, print unix commands to rename it"""
for oldname, newname in f.iteritems():
if oldname != newname:
print("mv \t\"{}\"\t\"{}\"".format(oldname,newname))
def fixfolders(topfolder):
"""given a folder, walk tree and generate list of files/directories that need to be
renamed so that they are kosher on a windows system, then print list of comands to fix"""
# note full path is (os.path.join(root, name))
# accumulators
filefixes={}
print "# --------- DIRECTORIES ---------"
dirfixes ={}
for root, dirs, files in os.walk(topfolder, topdown=False):
for dirname in dirs:
# dirpath = os.path.join(root, dirname)
if( dirname.startswith('.')):
pass # print('skipping {}'.format(dirpath))
else:
newdirname = fixpathname(dirname)
if newdirname != dirname:
dirfixes[os.path.join(root, dirname)] = os.path.join(root, newdirname)
# create dict of current folder
newfilenames = {}
for fname in files:
newfilenames[fname] = fname
for fname in files:
# fix the name, if necessary
newfname = fixpathname(fname)
if newfname != fname:
# make sure it's not already in the folder listin
if newfname in newfilenames.values():
newfname = makeunique(newfname,newfilenames.values())
# update the curr dir list
newfilenames[fname] = newfname
# and add to global fix list
filefixes[os.path.join(root, fname)] = os.path.join(root, newfname)
printfixes(filefixes)
printfixes(dirfixes)
if __name__ == '__main__':
if len(sys.argv) < 2:
sys.exit('Usage: %s foldername' % args[0])
fixfolders(sys.argv[1])
# test_fixnames.py
# a few tests of file renaming using standard python unittest
import unittest
from fixnames import *
class TestFixNames(unittest.TestCase):
def test_fixonefile(self):
badfilename="a_B?c-d.txt"
fixedfilename="a_B_c-d.txt"
self.assertEqual(fixpathname(badfilename), fixedfilename)
def test_parens(self):
filename = "a (nice) file.txt"
# should not fix
self.assertEqual(fixpathname(filename), filename)
def test_okchars(self):
filename = "ok characters are -.()!#$&{},~+.xyz"
self.assertEqual(fixpathname(filename), filename)
def test_badchars(self):
# filename = 'bad characters are \-/-:-*-?-\'-\"-<->-|.xyz'
# fixed = 'bad characters are _-_-_-_-_-_-_-_-_-_.xyz'
badchars = "@/:*?\'\"<>|"
template = 'fileXname.txt'
for c in badchars:
filename = re.sub('X',c,template)
fixedname = re.sub('X','_',template)
# concept - loop through bad chars to test them
self.assertEqual(fixpathname(filename), fixed)
def test_fixsomefiles(self):
desiredfilenames = {'ok.txt': 'ok.txt',
'ok-with-dashes.txt': 'ok-with-dashes.txt',
'ok,with, commas.txt':'ok,with, commas.txt',
'ok with & ampsersands.txt':'ok with & ampsersands.txt',
'not?ok$.txt': 'not_ok$.txt',
'ok with spaces.txt': 'ok with spaces.txt' }
fixedfilenames = fixfiles(desiredfilenames.keys(),{})
# original names stay the same...
self.assertEqual( set(desiredfilenames.keys()), set(fixedfilenames.keys()))
# and each one is fixed
for f in desiredfilenames.keys():
self.assertEqual( fixedfilenames[f],desiredfilenames[f])
# def test_folderfix(self):
# """concept for a test of real folder"""
#
# create temp dir testfolder=/tmp/randomfolder
# os.mkdir(path)
# add files to it from the desiredfilenames using touch
#
# result = fixfolder([testfolder])
# use this to debug the test suite is working
# def test_that_fails(self):
# self.assertEqual(fixpathname('abc.txt'), 'nothin')
def test_folder(self):
fixfolders("~/tmp")
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment