Welcome, guest | Sign In | My Account | Store | Cart
NOTE: Recipes have moved! Please visit GitHub.com/activestate/code for the current versions.

equivalent to doing $ find -name "*ext" -exec (some python function) {} ';'

Python, 141 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env python
"""Execute a python function for a selected set of files.

The purpose of this script is to build a filelist, by:
- recursing over a directory, or
- selecting files having a common filename extension, or
- just accepting the filenames as command line arguments,
and then executing any user defined function over this list of files.

This would be the equivalent of something like:
    $ find -name "*ext" -exec <some python function> {} ';'
using the GNU find(1) command.

An example function (which was my primary motivation to write this script) is
also included. This function applies a set of regex rules to rename the files
in the selected filelist.

Please let me know if you find this script useful or if you find any bugs.

Note: Since this general purpose script meant to be customized for your own
specific requirements, I have not done more error handling than I thought
necessary.
"""
__author__ = "Steven Fernandez <lonetwin@yahoo.com>"

import os, sys
import re
import fileinput
import getopt

Usage="""

    %s [options] FILE [...]
where options can be:
    -h              | --help                    Show this help text.
    -r              | --recurse                 Operate on files recursively.
    -f [<type>|all] | --filetype=[<type>|all]   The extension of the files to
                                                operate on. By default, the 
                                                program operates on all files.
                    
Eg: %s --recurse -f mp3 * /my_songs/*
""" % (sys.argv[0], sys.argv[0])


# The rename function invokes this editor to allow the user to make additional
# modifications to the new filename.
EDITOR = 'vi'

def rename(root, filelist):
    """rename(root, filelist) -> None
    
    Sanitize the filenames given in 'filelist', which are rooted at 'root' by
    using a set of regex rules.
    !!! NOTE: This function calls os.tmpnam() which is insecure.
    """
    if not filelist:
        return
    def apply_rules(filename):
        rulez = [('_+'       , ' '),     # One or more underscores to spaces
                 ('-{2,}'    , '-'),     # Two or more hyphens to single hyphen
                 ('&'        , 'And'),   # An ampersand to 'And'
                 ('(-)(\w*)' ,r' \1 \2')]# Spaces around hyphen seperated words
                         
        for look_for, replacement in rulez:
            filename = re.sub(look_for, replacement, filename)
        # Capitalize first letter of every word
        filename = " ".join([ word.capitalize() for word in filename.split() ])
        return filename
    
    names = []
    for filename in filelist:
        basename = os.path.basename(filename)
        names.append(os.path.join(root, apply_rules(filename)))
    try:
        dest = os.tmpnam()
        fl = open(dest, 'w')
        fl.write("\n".join(names))
        fl.close()
        os.system("%s %s" % (EDITOR, dest))
        ans = 'no'
        for oldname, newname in zip(filelist, open(dest).readlines()):
            oldname = os.path.join(root, oldname)
            newname = newname.strip()
            if oldname == newname:
                print "No change from %s to %s ...skipping" % (oldname, newname)
            else:
                print "Changing %s to %s" % (oldname, newname)
                if not ans[0].lower == 'a':
                    ans = raw_input("Contine (Yes/No/All) ? [N] ") or 'no'
                    if ans[0].lower() in ('a', 'y'):
                        os.rename(oldname, newname)
                else:
                    os.rename(oldname, newname)
    finally:
        os.remove(dest)

def main(root, filelist):
    """main(root, filelist) -> None
    
    Override this function, to do whatever you please with the list of files
    passed in filelist. The default behaviour is to call the function rename(),
    which applies a set of rules to sanitize a filename.
    """
    #print "got %s: %s" % (root, filelist)
    rename(root, filelist)

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print Usage
        sys.exit(0)

    recurse  = True
    filetype = "all"
    try:
        opts, args = getopt.gnu_getopt(sys.argv[1:], "f:r", \
                                        ['filetype=', 'recurse'])
    except getopt.GetoptError, msg:
        print msg
        print Usage
        sys.exit(1)
    for o, a in opts:
        if o in ('-h', '--help'):
            print Usage
            sys.exit(0)
        elif o in ('-f', "--filetype"):
            filetype = a
        elif o in ('-r', "--recurse"):
            recurse = True
    filelist = []
    for fl in args:
        if os.path.isfile(fl):
            filelist.append(fl)
        if os.path.isdir(fl) and recurse == True:
            for root, dirs, files in os.walk(fl):
                if filetype != 'all':
                    files = [ f for f in files if f.endswith(filetype) ]
                main(root, files)

    if filetype != 'all':
        filelist = [ f for f in filelist if f.endswith(filetype) ]
    main(os.getcwd(), filelist)