Let's say I want to make my digital images all lowercase: mv *.JPJ *.jpg

As we know, this doesn't have the desired effect: the shell nicely matches any 
existing item ending in .JPG or .jpg . If the last matched name happens to be a 
directory every 'thing' else will be moved into it, but still being called .JPG . 
That's just one possible scenario, most likely the command will fail.

So what the shell needs is a *friendly* utility that:
  a) takes a given source pattern
  b) builds a set of matched filenames
  c) takes a replacement pattern for the destination files.
  d) Based on (a-c) this tool will then generate one unique destination filename for 
each source filename, thus creating a set of source:destination pairs.
  Actually, several powerful shell tools are set up to handle such tasks (find with 
-exec {} ; xargs ; shell loops). But the emphasis is on *friendly*. Let's assume a 
slightly more complicated example, like renaming files *BAR*FOO9[0-9].HTM from the 
1990s to *bare*foot_199[0-9].html . I'd guess that only distinguished veterans of 
regular expressions can enter the substitution pattern straight on the command line, 
getting it right the first(!) time. The rest of us will have to try several times -- 
or worse, end up with a wrecked file system.

The attached python program wc2fn.py can handle complex renaming using a fairly intuitive syntax. Plus much more. wc2fn stands for wildcards-to-filenames. I also created a couple of symlinks to wc2fn.py to mimic the syntax of familiar programs:
wccp -- the copy (cp) version
wcmv -- the move (mv) version
wcecho -- the echo version
wcll -- the equivalent to 'ls -l'
The leading 'wc', again, stands for the wildcard version of the command that it wraps.
wcecho is useful when you want to check if the destination pattern really leads to the desired replacement; you echo the source:destination pairs to the screen first, rather than jeopardizing your file system.
wcll is useful to check if files with the generated destination filename exist; you either get them long-listed as pairs, or a corresponding error message.


Coming back to the slightly more complex example from above you would say:
wcmv '*BAR*FOO9[0-9].HTM' '*bare*foot_199?.html'
It's that simple. If you are more cautious you'd say first:
wcmv -t '*BAR*FOO9[0-9].HTM' '*bare*foot_199?.html' The -t (test) options prints the move command for each source:destination pair to the screen rather than executing them.
Note that I used single quotes to prevent the shell from expanding the wildcards. The reason for using '?' and not '[0-9]' in the destination pattern is given in the next paragraph.



ABOUT REPLACEMENTS IN DESTINATION: ================================== To my knowledge, there are no general rules in Unix how to 'match' patterns of destination files that do not yet exist. So I came up with the following:

A) The method must guarantee that the inverse of an operation yields back the original filenames. In our example
wcmv '*bare*foot_199[0-9].html' '*BAR*FOO9?.HTM'
does this. For exceptions from the inverse rule look for (*X*) throughout the text.


B) Replacements are done on a positional basis. Source and destination must have the 
same wild/tame-card patterns. Only that ensures that the generated destination names 
remain unique (to prevent accidental overwriting). I call the plain text fragments 
('BAR', 'FOO9', '.HTM') tame-cards in contrast to the wildcards '* ? [...]'. So the 
'card' pattern for '*BAR*FOO9[0-9].HTM' is 'wtwtwt' (note, the '.' dot within a 
filename is nothing special in Unix).
  You can look at how pattern splitting is done by entering non-matching 
source/destination patterns: the error massage will tell you!

If a fragment in the matched filename is a tame-card the source fragment gets replaced 
with the destination fragment at the corresponding position.
  If a fragment in the matched filename stands for a wildcard then that fragment 
replaces the wildcard in the destination at the corresponding position. Therefore, 
wildcards in the destination pattern are just markers with now further meaning; they 
can be any of the two '*' or '?' but not '[...]' (see code why).
  If you like to see how individual filenames are split into fragments, how the 
fragments are pair wise replaced, and which of the cards is more greedy, you can run 
any of the wc... commands with the -d (debug) option.


C) I defined two special symbols for the destination pattern.

  (anyText) -- The wildcard fragment of a matched filename is replaced by anyText ; 
anyText can be also the empty string which would be coded as ().
  In general, this can create destination filenames that are not unique anymore (*X*). 
It's up to the user to run a test first using the -t option, or wcecho.

  '|' -- Replaces a tame-card in the source with an empty string. This is needed 
because there are no simple ways of expressing an empty string within the destination 
pattern.
  When using '|' the generated filenames may be still unique, but in general, there is 
no way to reverse this (*X*).

D) Since people always ask for " cp * *.bak " I made that possible too:
wccp '*' '*.bak' Although inconsistent with rule (B) this shortcut can come quiet handy. It only works for destination patterns with one '*' and optional pre/post tame-cards. In cases where the 'twt' patterns of source and destination match, the systematic rule (B) supersedes. So there is not ambiguity.
For the curious, the reversal would be:
wccp '*.bak' '*|'


OK, this is getting much longer than planned. Next chapter would cover the usage of 
wc2fn.py in the general form, wrapping arbitrary commands with options; plus 
applications where generated destination file names match already existing files. I am 
going to skip this, throw in the Usage message, and move on to a real-life example.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage: wc2fn.py options 'command [cmd-options [$i[,$j] ]] $m $n' 'pattern1' 'pattern2'
     options:
       -v  -- verbose
       -t  -- test; print command to stdout instead of executing it
       -d  -- debug (automatically sets -t)
     command, cmd-options  -- see documentation of shell command or program
     $i,$j,$m,$n  -- any combination of $1, $2 ; any sequence or repetition
     $1 -- file matching pattern1 : $2 -- filename generated from pattern2

Quote 'command cmd-options $i $j' 'pattern1', 'pattern2'
     'single quotes' on Unix, "double quotes" on DOS

See also sym-links: wccp, wcmv, wcecho, wcll ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

REAL-LIFE EXAMPLE:
==================
I am going to demonstrate some renaming and 'touching'.
Since I am anal about time stamps I like my edited images having the same time stamps 
as the original photos (at least for events where the sequence matters). My originals 
come as .JPG files but for my edited images I prefer .jpg (there is a reason to keep 
.JPG for the original files).

Here is the directory structure of my image directory:
  img/IMGPnnnn.JPG              # original digi image; nnnn={0001..9999}
  img/EC2004/longName_nnnn.jpg  # modified image, but preserved nnnn
  imp/tm_1100-1299/tm_nnnn.jpg  # empty file with time stamp of IMGPnnnn.JPG

I'll work from img/ without changing directories.
First I make sure all files in img/EC2004/ are lower case .jpg
     wcmv 'EC2004/*.JPG' 'EC2004/*.jpg'

Next I stamp the edited files to the original time:
     wc2fn.py 'touch -r $2 $1' 'EC2004/*1???.jpg'  './(IMGP)1???.JPG)'
Done.  (the nnnn for relevant files is larger 1000)

In reality however, my edited images have already left the machine where I host the originals, and eventually I'll also have to move my images from PC onto CD. So I'll create a light-weight package of time stamps which (when tar'ed up), can be easily transferred by email, scp, ftp, ...:
wc2fn.py 'touch -r $1 $2' './IMGP1[1,2]??.JPG' 'tm_1100-1299/tm_1???.jpg' A few comments about the above line:
- command, source- and destination pattern are single-quoted.
- The source are the IMGPnnnn.JPG files ( '1[1,2]??' covers images 1100-1299).
Each matched file is used as $1 in the touch command.
- The destination are the, to be created, tm_nnnn.jpg of 0 byte length.
Each generated name is used as $2 in the touch command.
- When traversing directories it's recommended to explicitly name the current as './' ; It's redundant here, but required when the filename starts with a wildcard (to have a leading 't' in the 'twt...').
- The touch command uses the mtime and atime from the reference(-r) $1 and creates $2 as an empty file since it doesn't exist.


Now let's pack them up:
tar -czf tm_1100-1299.tgz tm_1100-1299
and ship the package to the server where I host some (but not all) of my edited images (longName_nnnn.jpg). Expand the tar ball in the html area:
tar -xzf tm_1100-1299.tgz Correct the time stamps on the server:
wc2fn.py 'touch -r $2 $1' './*_????.jpg' 'tm_1100-1299/(tm)_????.jpg'
It's that simple. The individual longName portions '*' are replaced with 'tm'. Note that $2 and $1 are in reversed order compared to the command that generates the time stamps: I only touch the files that do exist by using my tool to select the corresponding tm_nnnn.jpg files.


Congratulations that you made it that far :-)  You are invited to check out the stuff 
talked about in above example:
     http://horsu.freeshell.org/bucket/EugCelibr2004/?M=A
     (slightly dated, but not yet outdated)

Enjoy !  -- Horst, interested in feedback:
    * general/unix related on this list
    * python related on
      http://www.euglug.org/mailman/listinfo/python
    * or off the list
  (no matter which avenue you'r choosing, include-entire-message-in-reply may be not a 
good idea :-)

=========================================
Assorted one-liners and further rambling:
=========================================
  * Special characters used by wc2fn are ' * ? [] () | $1 $2 '
  * ' [char-range]' can not be used in destination pattern (use '?' instead)
  * '() | $1 $2 '   can only be used in destination pattern
  * ' ~ $HOME $anyEnvVar ' are not supported
  * 'command' 'source' and 'destination' need to be single-quoted to avoid expansion 
of special characters by the shell.
  * when traversing directories use './' to indicate the current directory
  * make use of options -t -v -d
  * using option -t you'll see that I quote the individual source/destination 
filenames. This allows to escape white space and special characters, e.g. to replace a 
space in Windows/Mac filenames:
     wcmv -v '* *' '*_*'
    or remove the space if desired:
     wcmv -v '* *' '*|*'
    If there are more than 1 space I TAB back until -v tell me zero files processed.

  * In the code, I am using the notation source:target rather than source:destination; 
for no particular reason --just too lazy to change it.

  * If you like to add your own customized symlinks (or modify the current ones) you 
only have to add one line to ' specialCmds = {...} 'in the code.
    Python is easy to read and to understand even with minimal programming background.

Advantages of doing the 'wildcard replacement thing' with one program in core rather 
than shell utilities piped together:
  * It's much easier to add test and debug options and provide the end-user with hints 
in response to errors
  * having access to the entire set of matched files in core allows for non-linear 
response (program flow based on the set of actually matched files, rather than a rigid 
source:destination treatment).
  * If you work on different platforms and shells (BSD, Linux, Solaris, Cygwin, bash, 
tcsh in my case) the minor differences in commands and options can become a major pain 
in the neck, especially if you don't have administrative privileges to bend things to 
your needs. Having one human interface to one program in an area under your control 
can remove many of those frustrations.
=========================================


HOW TO GET wc2fn.py
===================
Since EugLUG opted against attachments I am including the code in the body of the message. The code looks cleanest using TAB space 4 (vi:set ts=4). In case your mailer wraps the long lines, or garbles up the TAB signs, I am also including the compressed source code, uuencoded -- just as we did it in the 80s (-: Nice work-around the no-attachment limitation, huh?
Save entire message to text file wc2fn.uu and run uudecode wc2fn.uu // this creates wc2fn.py.gz gzip -d wc2fn.py.gz // this creates wc2fn.py



HOW TO INSTALL: =============== Make executable: chmod +x wc2fn.py Test path in #!-line: ./wc2fn.py If you see the Usage message you are OK. If you see an error message try which python and replace the part '/usr/bin/env python' with the output from 'which python'. Move the program to an area that's in the $PATH, e.g. /usr/local/bin/ , go there and make the symlinks (you may need to be root) mv wc2fn.py /usr/local/bin/ cd /usr/local/bin/ ln -s wc2fn.py wcecho ln -s wc2fn.py wcll ln -s wc2fn.py wccp ln -s wc2fn.py wcmv That's it for simple command line usage. If you also code in python you may want to put the wc2fn.py into an area that's in your customized sys.path (personally, I like /usr/local/lib/pylib/); then you create a link /usr/local/bin/wc2fn.py => /usr/local/lib/pylib/wc2fn.py This way both python module import, and command line execution always use the same file; even after it got revised.

################ begin code ####################
#!/usr/bin/env python

""" wc2fn.py             (wildcards-to-filenames)
This module consists of the class Wildcards2Filenames,
and a front-end for command line usage in __main__.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, Inc.
  59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
  http://www.gnu.org/copyleft/gpl.html
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.

Horst Lueck, 2004-10-30

TODO:
        consider processing 'things' other than files
        consider option/need(?) to not quote 'source'/'target' files (for $HOME, etc.)
"""

import os, sys, string
from sys import argv
from getopt import getopt
from os.path import isfile, isdir, islink
from glob import glob
from fnmatch import fnmatch

wildcards = [ '*', '?' , '[', '(' ]             # regarding '(' see __doc__of class
noChar = '|'    # placeholder for '' (no character); used in target pattern

# the following options can/may be overridden via parse() from command line:
verbose = 0             # report number of files processed, or skipped,...
testing = 0             # do not execute command --print to stdout instead
debug = 0               # show source and target fragments (switches automatically to 
testing)

class Wildcards2Filenames:
        """ This class handles wildcard expansion of existing filenames (source),
                and generates a set of source:target filename pairs in which the
                target names are modified according to the target pattern.
                The target files can already exist or may be generated at a later time.
        Expansion of source names is done by module glob, mainly along the Unix 
convention:
                wildcards: '*', '?' and '[char-range]' .
                recognized: '.' and '..'
                not recognized: '~', shell variables like '$HOME'.
                not supported: patterns with two '**' in a row.
        Target file names, special characters:
                simple substitution: '*' or '?' but not '['
                        ('*' and '?' are just wildcard markers, so either will work)
                special symbols:
                        '(someText)'  --  replaces a wild source fragment with 
someText,
                                this includes '()' for inserting the empty string
                        '|'  --  noChar; replaces a tame source fragment with empty 
string ''.
        How it works:
        a) Both source and target patterns are split into wild- and tame-cards.
        b) Matching source files are gathered (currently, directories and symlinks are 
ignored)
        c) Source filenames are split into fragments according to source pattern.
        d) Target filenames and fragments are generated based on the following rules:
                Replacements are done on a positional bases.
                Wildcards in the target pattern are replaced by the fragments of the
                        source filename that matched the wildcard at the corresponding 
position.
                Tamecards in the target pattern replace the tamecards fragments of the
                        source filename at the corresponding position.
                Therefore, wild-/tame-card pattern of the target has to match that
                        of the source. (However, see (*X*)exception)
                For simple substitution the kind of wildcard in the target pattern is 
irrelevant
                        and can be either  '*' or'?'  , but not '[' .
                For special substitution using '|' and '(someText)' see above.
        e) Using the class:
                The caller creates an instance of this class using source and target 
pattern.
                All relevant data structures are built at instance creation, and under
                normal conditions there is no need to access any of the class methods.
                The caller accesses the class' data members produced in steps (a-d) to
                make the desired os.system() calls for each source:target pair.

        (*X*)exception: target pattern contains only one '*' as wildcard and 
source/target patterns don't match.
         In this case the entire source match replaces the single * in the target 
pattern.
         This was added to conveniently type  wccp '*' '*.bak'

        To watch source/target split/replacement for matched files in action add the 
options -dv
         (debug, verbose) to the command line when calling any of the programs in 
__main__
        To see how source/target patterns(not the matched files) are split enter 
non-matching
         source/target patterns for one of the programs in __main__ , and the error 
message will tell you.
        """

        def __init__(self, sourcePattern, targetPattern,
                                 fileTypes=[isfile], verbose=verbose, **kwargs):
                """
                sourcePattern    - pathname(string) with wildcards to be matched
                targetPattern    - pathname(string) with replacement pattern
                fileTypes                - file types to be processed (not yet 
implemented)
                verbose                  - report and warn to stdout
                """
                self.spat = sourcePattern
                self.tpat = targetPattern
                self.types = fileTypes
                self.verbose = verbose

                # split source/target patterns into lists and symbolic representation, 
like 'wtwwt':
                self.spatL, self.sp = self.splitPattern(self.spat)
                self.tpatL, self.tp = self.splitPattern(self.tpat)
                self.all2one = 0                # boolean; see next couple of lines
                if self.sp != self.tp :
                        # allow things like (* => *.bak) or (*x*y[0-9] => 
tempDir/*.tmp) :
                        if self.tp.count('w') == 1 and (self.spatL == ['*'] or 
self.sp.count('w') > 1) :
                                if self.tp.count('wt') == 1 : self.spatL.append('')
                                if self.tp.count('tw') == 1 : self.spatL.insert(0, '')
                                self.all2one = 1                # sp and tp become 
meaningless
                        else:
                                errorExit( " pattern mismatch:\n  %s  %s <> %s  %s \n" 
% (self.spatL, self.sp, self.tp, self.tpatL) )
                # The core lists, in the order of processing:
                self.sourceFiles = []           # filenames that match sourcePattern 
and fileTypes
                self.sourceFrags = []           # list of fragments (wild- and 
tame-cards) that constitute the source
                self.targetFrags = []           # list of fragments that build the 
target
                self.targetFiles = []           # target filenames after replacing
                self.__globReplace()
                if self.verbose:
                        print " - processing %s files" % len(self.sourceFiles)


def __globReplace(self): """ Build the member lists for sources and targets. """ for file in glob(self.spat): typeFound = 0 ##note, if a symlink points to a file/dir, both, islink and isfile/isdir are true ##for ftype in self.types : // not yet implemented if islink(file): if self.verbose: print " * ignoring symlink " + file elif isdir(file): if self.verbose: print " * ignoring directory " + file else : self.sourceFiles.append(file) self.sourceFrags.append( self.splitSource(file) ) self.targetFrags.append( self.__buildTarget(self.sourceFrags[-1]) ) self.targetFiles.append( string.join(self.targetFrags[-1], '') )


def splitPattern(self, pat): """Split pattern 'pat' into a list of wild- and tame-cards. Return tuple (patternList[], 'wt..') 'wt'=wild/tame. """ # Check for matching numbers of parenthesis: (bad placement is not caught) for pars in ( ('[',']'), ('(',')') ): p0, p1 = pars[0], pars[1] if pat.count(p0) != pat.count(p1) : errorExit( " mismatch in args: %d '%s' <> %d '%s'" % (pat.count(p0), p0, pat.count(p1),p1) )

                # Dissect the pattern:
                pL = []                         # pattern list to be returned
                cp = ''                         # card pattern: 'w', 't' / wild, tame; 
to be returned
                sTmp = ''
                for i in range(len(pat)):
                        if pat[i] == ']' or pat[i] == ')' :             # close 
pattern and append parenthesis
                                pL.append(sTmp + pat[i])
                                sTmp = ''
                        elif pat[i] in wildcards :
                                cp += 'w'
                                if sTmp :               # close previous pattern
                                        pL.append(sTmp)
                                        sTmp = ''
                                if pat[i] == '[' or pat[i] == '(' : # open pattern 
with leading parenthesis
                                        sTmp = pat[i]
                                else:                   # single wildcard
                                        pL.append(pat[i])
                        else:                           # gather chars of tame- or 
wildcards
                                if not sTmp : cp += 't'
                                sTmp += pat[i]

                if sTmp :
                        pL.append(sTmp)
                ## remove check below when stable:
                if string.join(pL,'') != pat :
                        raise " failed to split pattern '%s' - got %s " %(pat, pL)
                return pL, cp


def splitSource(self, src): """Split source filename into a list if wild- and tame-cards. src -- a single matched filename return -- list of cards """ # The formulas are getting longer, so the variables want to be short and cryptic spL = self.spatL # source pattern list (common to all source files) ssL = len(spL) * [''] # split source as a list ssI = len(spL) * [-1] # index within src that leads to ssL WC = wildcards # aliased for brevity wasWC = 0 # previous element in ssL was a wildcard wasAstar = 0 # previous element in ssL was a '*' pos = 0 # varying char position in src

                for n in range(len(spL)):
                        count = 0                               # for debugging, 
remove when stable
                        leftOK = rightOK = 0    # left match contains spL[n]
                        if spL[n] == '*' :
                                wasAstar = 1
                                continue
                        if spL[n] == '?' or spL[n][:1] == '[' :         # allow for 
spL[n]=''
                                while not (leftOK and rightOK) :
                                        leftOK  = fnmatch( src[:pos+1], 
string.join(spL[:n+1], '') )
                                        if leftOK:              # only then test right
                                                rightOK = fnmatch( src[pos+1:], 
string.join(spL[n+1:], '') )
                                        pos += 1
                                        if (leftOK and rightOK) :
                                                ssI[n] = pos            # 1 past 
match, so we can slice[x:pos]
                                                if wasAstar :           # get previous 
'*' match
                                                        ssI[n-1] = pos -1
                                                        wasAstar = 0
                                        if count > len(src) :        ## remove when 
stable
                                                raise "exceeded loop count for '%s', 
pattern '%s'" % (src ,spL[n])
                                        count += 1
                        else:
                                while not (leftOK and rightOK) :
                                        pos = src.find(spL[n], pos)
                                        lenp = len(spL[n])              # for brevity
                                        leftOK =  fnmatch( src[:pos+lenp], 
string.join(spL[:n+1],'') )
                                        if leftOK:                              # only 
then test right
                                                rightOK = fnmatch( src[pos+lenp:], 
string.join(spL[n+1:], '') )
                                        if (leftOK and rightOK) :
                                                if wasAstar :
                                                        ssI[n-1] = pos
                                                        wasAstar = 0
                                                pos += lenp                     # take 
a big step
                                                ssI[n] = pos
                                        else:
                                                pos += 1                        # find 
real next pos at next loop
                                        if count > len(src) :        ## remove when 
stable
                                                raise "exceeded loop count for '%s', 
pattern '%s'" % (src ,spL[n])
                                        count += 1
                if wasAstar :
                        ssI[n] = len(src)
                rtn = []
                for i in range(len(ssI)):
                        if i == 0: rtn.append(src[:ssI[0]])
                        else: rtn.append(src[ ssI[i-1] : ssI[i] ])
                return rtn


def __buildTarget(self, srcFrags): """ Build target filename from source- and target-pattern and matched source filename srcFrags -- self.sourceFrags[-1] return -- target filename as list of cards """ rtn = [] spL = self.spatL # source pattern list tpL = self.tpatL # target pattern list tpLw = [] # scan for wildcards only once for pat in tpL : if hasWild(pat) : tpLw.append(1) else : tpLw.append(0)

                # convenience method to allow things like  (* => *.bak) or (*x*y[1-9] 
=> *.tmp)
                if self.all2one :
                        for n in range(len(tpL)) :
                                if tpLw[n]:
                                        rtn += srcFrags
                                else:
                                        rtn.append(tpL[n])
                # the normal case:
                else:
                        for n in range(len(tpL)) :
                                if tpL[n] == noChar :
                                        continue
                                if tpL[n][0] == '(' :
                                        rtn.append(tpL[n][1:-1])
                                        continue
                                if tpLw[n]:
                                        rtn.append(srcFrags[n])
                                else:
                                        rtn.append(tpL[n])
                return rtn


def hasWild(term): for card in wildcards: if term.count(card): return 1 return 0


def errorExit(message=''): sys.stderr.write('\n' + message +'\n') sys.exit(1)

######### stuff below for command line usage #########

def parse(optargs=sys.argv[1:]):
        global verbose, testing, debug
        try:
                opts, args = getopt(optargs, 'dtv')
        except:
                if argv[0].count('wc2fn') : errorExit(usage)
                else: errorExit(cmdUsage)
        for opt in opts :
                if opt[0].count('-d') : debug = 1
                if opt[0].count('-t') : testing = 1
                if opt[0].count('-v') : verbose = 1
        return (opts, args)

usage = """\
Usage: %s options 'command [cmd-options [$i[,$j] ]] $m $n' 'pattern1' 'pattern2'
     options:
       -v  -- verbose
       -t  -- test; print command to stdout instead of executing it
       -d  -- debug (automatically sets -t)
     command, cmd-options  -- see documentation of shell command or program
     $i,$j,$m,$n  -- any combination of $1, $2 ; any sequence or repetition
     $1 -- file matching pattern1 : $2 -- filename generated from pattern2

Quote 'command cmd-options $i $j' 'pattern1', 'pattern2'
     'single quotes' on Unix, "double quotes" on DOS

See also sym-links: wccp, wcmv, wcecho, wcll """ % tuple(1*[argv[0]])

cmdUsage = """\
Usage: %s  'wildsource'  'wildtarget'
  quote wildsource and wildtarget -- 'single quotes' on Unix ("double quotes" on DOS)
""" % argv[0]

specialCmds = {
        "wcecho" : "echo $1 :: $2",
        "wcll"   : "ls -l $1 $2 ; echo '' ",
        "wccp"   : "cp -p $1 $2 ",
        "wcmv"   : "mv    $1 $2 ",
        }

if __name__ == '__main__' :
        opts, args = parse()    # note, parse() sets options globally
        if debug : testing = 1  # to be careful
        cmd = pat1 = pat2 = ''

        # look for special commands:
        for scmd in specialCmds :
                if argv[0].count(scmd):
                        try:
                                pat1, pat2 = args       # may catch forgotten quotes
                                cmd = specialCmds[scmd]
                        except: errorExit(cmdUsage)
        # no special command found -- try sys.argv[1] as command:
        if not cmd :
                try: cmd, pat1, pat2 = args             # may catch forgotten quotes
                except: errorExit(usage)
        wcf = Wildcards2Filenames(pat1, pat2, verbose=verbose)
        for n in range(len(wcf.sourceFiles)) :
                if debug: print "%s :: %s" %(wcf.sourceFrags[n], wcf.targetFrags[n])
                cmdn = cmd.replace('$1', "'%s'" % wcf.sourceFiles[n])
                cmdn = cmdn.replace('$2', "'%s'" % wcf.targetFiles[n])
                if testing: print cmdn
                else: os.system(cmdn)
        sys.exit(0)

################ end code ######################

begin 711 wc2fn.py.gz
M'XL("-1`A$$``W=C,F9N+G!Y`,U:ZW+C1G;^33Q%KT8N`!J0$L=)I983V37K
MR]JU8X_CD<O9HE4J$&R*L$``"S3%8>+DV?-]I[L!D*(\3G[%51Z!0/>YW[M?
M_.ERVS:7B[R\U.6CJO=F795!<'9VIG;9JU4YJ?>C4;3+BV66-LMV;*KQ*B]T
MF6YT&P<WZ[Q5FVJY+;3*JK+-6].J:J7,&K^+M&W5SW[GJZ_]MB1(RZ5*U:JI
M2C/6>%Y5#;9O-GQ?Y*56VS:]URHOU=W=)LW+N[M)8''5377?I!N%QU6CM6JK
ME=FEC7ZM]M5696FI&KT$%4V^V!I`,`HP+ZLF`)'Y:L\7VW*I&Z'0Z&;3D?O7
M[W]2?]6E;M)"_;!=%'FFWN:9+EL=I,#+-^U:+]5B+\N_)O;[EMAIL PROTECTED]
MF:AORVP2J'_^L[K1FQIR^:%(,ZW&ZOTV!T6??GJ5J+]4K>'2[]XH=?5J.IV.
MIY]>_4NB?GK_)E!K8^K9Y>5NMYO<E]M)U=Q?9E6]+_3*7-[7Q61M-L438?0\
M+RDV4KBN:K"[EMAIL PROTECTED]@(+M:[EMAIL PROTECTED];)`%6JI^_O?GFW4\WZLWW?U<_O_GQQS??
MW_P=<OZF:EJCWFYU]I"H5U=7_S2>7HT_O8("WGWY;A:,1,\4(=!GNFWS\EZ%
M9HT_;:@J8*9PH0B:23M87M64SV6I]3+Z/%:F4F5EU#^V%:02MM6VR71X&9JT
MN=<FM+M51,LX_^;==U\E2IML$M,[EMAIL PROTECTED]'_!>W@<PJ`U?*/<=
MH![M2X`$>O_>_K)?JG92IV;M/^4M$2?XN\P;_H$Y/[EMAIL PROTECTED];7H(.#9OEV5F]1D
MW7[W,[EMAIL PROTECTED]'7:J["BS!1X>>APK]S/D:ANAV-7L!:[[%(1(A7+6SJ[FY997=W
ML$IQH*"LOEBG#:"$OX784-.:UE5!B5(X8:BBLE(9UJ09##I^31U;(Q!1*K"'
M]_#I%V(6JZHHJATQ6H6T])K+3;JG>52/NFGRY5*7ZC%/L;5I=10KX73HH+,`
M"Q=5JT'7E>5#V"^WFP55O7+Z<R:BEXD"K>[EMAIL PROTECTED]&M(1%N_]):@_Z@
M,_JMQS4>U]"LH;&T9EG!:/.R-3I=!DN]V,INQ>WMNMHI:T-T>,_ZJDGO-[I$
M3(K:[EMAIL PROTECTED]:RKH*,[EMAIL PROTECTED],D#H)G(Q:[EMAIL PROTECTED],[EMAIL 
PROTECTED]:!7,RBO4QAZ
M59)Y_2&WW'[EMAIL PROTECTED](=7$2C$:D\%Y"C2%!4+OA+KMBYFEW6Z&$O&FIS]TZAZ%!
MAP#A%EG8C$`2WW(H/LVRREH4>6.4.S"#"3;?]&[EMAIL PROTECTED]/>6=FK+F80G
M%)#A4:K`(QP\WVA`^FK(M!._)8DAJ4(@1[1TZ8$NDRA&<[EMAIL PROTECTED]"#I^*O,/
M3!V/4!,`0=*CSG5FO=]09.&<5CYNTO)>WX:*G#0ZJ^[+_#_T$FLG;MED$N(3
MS>[EMAIL PROTECTED]"([EMAIL PROTECTED]"CP28<.(VM=N:MLP]3F94,[EMAIL PROTECTED]"*D
M+E+55#OLN.G%:#D'CEIG.9)(YY(TGU&;2S)HMPM8AMD*KV2/DB:##,C$CO"`
MU:.(GX0;,@_]_KIM36]MF[1Y`&[EMAIL PROTECTED])>9*A-]5S4-,;(Z&=K]95(40,`IA
[EMAIL PROTECTED]"1*RUZ#W&LNUWT3#A=W1!?(R*[9+[`LC@&([EMAIL PROTECTED]
M1BR>.D7>,WL?E(GY-X?0QK+70\2&)GX2\1`*PAR$_0V<G+D,3)*E-$8B%0J/
M_;[3&R77UD7.N`%W()=CMW"CQV)[EMAIL PROTECTED](U7>,VD3D21&_X/;[E.*%^4?9MFE`
M7;%/D&YA6J9J<BX".,B9B<+NR._+"[EMAIL PROTECTED]&B=#!ZYUU0%([EMAIL PROTECTED]<UY'1.^TR
M5C>'@<%B'NQOALZZ2)D%JO(HYC=P13&''ZT*^JWBKA4MNZ[:G/8)^R&4=J*P
MOHN*OKPX%+6`<&KM:J2>-EMCT1;:0WG8`D5R)O9Q4V?C>"[EMAIL PROTECTED]
M;1+&L/_W2'+DN$]^[1\AZN.H:1(P?%0*8E.7G3UUV%U9Z6A:HX:$6FVM0)Z)
MUBVQV"<[EMAIL PROTECTED],X4^#2,"XR&'+0!>)[W3(J'S
[EMAIL PROTECTED]"/Z:ED$`38O1'H'=QQ`4EQAU4*X.P)`%7:/#!94C$UM:!O[FH-8PUY"9=
MH+0``!VKGUH?)R25SEQ"8CX&[EMAIL PROTECTED])B*:D^+:$7D5.7>BVF9YV>5+Y!*/1<
M*M3E*</(-C/;[EMAIL PROTECTED]<`A>J=\)4GH$R0?-AJ&<)F"+)0D&E".*+I:R
MU"H\%T4.-NX/>Y^-1C,E$6;(H5VMVWYA:&G<:!9/4C0MMYFMX5#LU"@>TO&2
M%3,`;=(':]&([EMAIL PROTECTED]&;U"G$4,K`5FGV?JHH&[EMAIL PROTECTED])^=&A8LV,3`:\&
M21KNP43-H""I:%#J2,`3T)?'\19!)'3N#*[1"CG%(8S8S(`DWW3QWGI#EPS$
M%Z!;V/?%:?LE3"G"=J`G19DJXK?50RZA69D]NAZTKEDM=(<7DT7Z$(+M&X1_
MP7=(NH3BRZ:/AB(^'Y!L&F"[EMAIL PROTECTED]/(\J7S>/D(DB(I1A/E:N+85UX'+>UN
MC:*:"J+U#DS%-7'ML-L5:NDV?5E[+.J(7LG]!Z3&@_0"[EMAIL PROTECTED]
M]$:>)?X_3Y.RGB%:;!K*"%;,+ET*#\.Z"BWX1"KE8(2$I5?8FY>YN;N+6EVL
M$H?\!XLT<;KU/Z6\$"YNH,+V>FY;L=M.K-?N;Z(N+A[0==^W,6.'[EMAIL PROTECTED]
MVV93QU@>V0(BMC5%WY5!18M.>%U!_5$`0T/QC=5HU%$]XDXI!FF''DO7!2G1
MVEY+)UH(%*D31HZUD>QWO12E#3;+OOGIN84X)RWPHP$ZX-Q_,_;;`5/=-Z'L
MNA>U_]"W=.X)'P+IKL2>GC$=J6(*&?RX2@@59YZ1"T1;<.C"JBVU=V:W,^%L
MR,-;YCMY)C?V"?@<U5&W+AXRYS>9W]EDAIO@>*]HX:[?7%15H=/RM7A9B3P%
M9]TRL\(#Z*X42K[J"/O3=8=.:ND7Z&!042D[]K"\11?J^C,E\29F51]=?+C8
MSZ_&?[[E>P3H^LN\N;R8F$T=6R@>@:DGP%Z:*-R%L;J^5E,19<_Z6[Z<(Y[=
M2AMM7P_W?*:F#N8IH,9#G:D>Y"1%+UXNHS",G]EG=B?WV5H_ND)CYK8>"7AJ
M;<:&[EMAIL PROTECTED]@(5X.X);BWE.U(%ZVV!$LT^>I#;B)UUF6A3=Z*;\Y^*97ZI)7_
M__4S__1+>:8^&<JG,Z'.++H'?(U5+'9\8VLZ;<TU\3D&5;>=6?0#K=Y`Q>B_
MEC0`%<C,IB_`^^+UT`UM97[L7PX6ZL\>5B'-]FHXJ3C5I,06%6=I4G'I0>'8
MN84XYL?`"QR6/\M!?CT"<<"M>=)UK)A7;"!D+O.;[^[8XKN^(HH'#N3"B:C;
MSG/.&&[EMAIL PROTECTED]:Z%CT<1#XA#+$P64^!ZB_=#S9.LK%)&8V"ZD=%(M2
[EMAIL PROTECTED](.$(PPZ6,=QDZQ/=?((AS1+CBZ,[EMAIL PROTECTED]<I%NQ;DVH%W*
M^'"!]M0/$06[S6F7,EV45([EMAIL PROTECTED]&XTN+]6)O.$BB(4>$6Y\
M&`&\V)67^85M3:5\=G2?J9="KW5)`0?2_K?0?#N\/X*'9#+K(\1`H3[X")XG
M*VC"[EMAIL PROTECTED]@UT#PS_<=7<[EMAIL PROTECTED]&S],?H5ZHV-D
M!"!!4'7V^20))<H;$JSMO>11'^)"/(0V?::=HSXSH4"[CN8%X4IR5.1`O,6F
M.4G8F<D$9##27Q."M*.]B;]07ZQU]M!7MM29'=5*%URGG&NLT5"T,Q4MTJ7J
MBQSI=!!YTNW]6K([EMAIL PROTECTED],1Q=G@;[EMAIL PROTECTED]>(SS&%(?U=&2(>@JOX>KYU6UB
M'Z:WSG#!A,LT]57,[EMAIL PROTECTED]@DX(4Y*C_9D@&Z#4_00/%W&`?)3,<P`;B
MJ^00>D(,L:]OOLQ1G&6VG':B)?;ZK0N"7..U)HJR95TC.A%'S%B&A*%=.AP#
MS*`4CC"AZ4M1;B**??T41'NSL4"<C'-R*;/.B"&19A3/>M'-\UOF9HA>B4*Z
M%VBX9T)%P5JN'B0D:]-#;8N(ZZX4$!)>.F#.,094V0#A4'$>W1735E<0PLMK
M\MO%#6X>$M/HQ[S:MH/*^0E^B_80[Q'+\V.6(["L7J`I0X?E&99J'26>'=T<
ML>S!6QC6T%B/B/I<"^JY.R9R(!V_!YOLE%!FOG:P1.\EF9V0/",R8A;!*"<P
M$_:B?MD1%0Q$&)P4TPL>NVRJ1Q0UXMX+S9)4^LS6<+X]4P[(((C5;Q-&+.MN
M%G*3YE#.F5JE"'W24;>'@8H.-E;W(!R)&OY%&<"[EMAIL PROTECTED]@YR`,NG#M
M6K\F.XR"QR.W81S,[EMAIL PROTECTED]/X'Z?)J5?5L`4FL)XL6>=CJU>##XHW,AEM-MLB
M]=-3(S-L'D[(&,ZV\?UQP2XMO?.W:]^C9<V^-GDF4_>W?3>"NM.:T\$DUQ(3
M<3)026>'[EMAIL PROTECTED]@A!R$BT\S"\/6[$.)*Q\I(=WQ[M0&[BCKQ<[EMAIL PROTECTED]
M#*PJ([EMAIL PROTECTED]>[EMAIL PROTECTED],D%F*%K0;\V>1S-I*XNO1BXJ>H_6
MA4L7)6':^<S0A?#B#8RR\7L_MA6]#B-PU7;(L`6*V%,_]+)N'*LL8X$+F^5A
MV*0X;-B4V-^CYUH9V]P#8N(=:>`\W,,S]W=_PZ8F1_:[EMAIL PROTECTED]
M\]*G-OM#8M-%Z*+C0`)3&RZQ-2]M^7>XYW,)</;%?#;MHMZL;SQ7W8)K%R-W
M:[EMAIL PROTECTED]&CFP:IR/<IU//$=M_>U(=47CS&:3YDM7,0;T#^+/R95_DC%P(LT"$
M&AD2&[EMAIL PROTECTED]@[EMAIL PROTECTED]'_'[EMAIL PROTECTED]>%S
MK=.3^/).RZ@;]7*FYQ_(\JW;RH#CU2-LR83#&RAU:$_U1P-,XZG#I<93_V%H
MY1W1UO`^L]Z),`@,?>0^,[EMAIL PROTECTED]&[EMAIL PROTECTED](S;85AFLGUB"<
M\.P6+[Z^W?YC=F(]#U`GJYPI1R`G9#?VAE36?<@A6N]5?;08>M`)>R.(9TWN
MI,4IZ[O_-Z,CNC]F=Q^SM`-S.6T4IRQ"N9?.K$G/R';8#XCF:I'?R[C_A#G;
M5[T.>]>PL8R'/XU."SO)XC<$>7FF]?R_LL0GLNOX]%0Q>YM22N_3I3!V]*5P
MSN!X-5/8TM5'-#""O;H=U&G'*Q17Y%38S#[>JMM!08/5_=#AN(V4>D9ZP./Q
MP]'=#7LC2?+U>#!^&`_+<E^]'%5$MMBQXQR6,:?:UT&A(VN.T:?M,\7/0,!_
MM&C!4M,O-=W2H^.C?NFNZYX(D)%W-:R'_?F2#+!L4RDU`)%XY:Y1;F"]-#]0
M$X%Z%4X[S1Y]N(H#J>ZZ4R$>-,E!G"NWCL:VS\QMIVYN:^>[EMAIL PROTECTED]&6GW,*C2<J
M#L.*HY_%DC38M_-;ROWE=6<]?>?1??9LF,[EMAIL PROTECTED]
MF7?+OO[HU\%INI[J&:+FTQE')\]".>1WX'/6;GU<^!C?!XY(/_36P&N;]#NY
M->K.NOL[0U957./:?;ZVX<)!1!AR3U<.<C]E<(=:**JXI=VWD]8L\7FR:W*C
MH_"7,D2'[(^^7O)W;-=I;H=5!B_\?XBFV]7*M67/7''M%EM"[(6[JJ93M=<$
MRSN,$/@MR>%X$C;0G8.YFVN)+62#D6GVY!/;VT0&)/!`>[EMAIL PROTECTED]:6YI'DFP/
M@)VT!,G5;[EMAIL PROTECTED](N_(3VN%XR0&WO+&WS(-LN?W#<Y1:S%BTF#<M#Q/[EMAIL PROTECTED]
M?W]O>G*-D37]'<'3JQYE57]LU2LVZF4`C5A17RO$OE\"(7;&3M8?XH9>+W/P
M,O9OY^?Y/#G_%5GA5IUOU#D4'[EMAIL PROTECTED];-),?",6/$H[],9I[:6R,
M!E.OW1#5(WYRO=%>(N1=2/*?&P]B*2"L[*+#FXRMAL3')I:[EMAIL PROTECTED]'-HOP
MID^VW?B#.;FY)[?B/#%5X\]_!=1Y#BDDYYODW+;3/+K&TD5>=OO/IXDZ?Z5>
MR[=6_V,K0;>20P)M;)=F84T)0:;MW1S2RQ2:!`SW6;)7?YE)TJB7N)-%\&_V
MYK`G>\CI>:[.?QWJ*SE66.A&!W+]F/>62[F)[EMAIL PROTECTED]&'1?3CCAR_?O0^"][S$
M4J"+:/>;[EMAIL PROTECTED];A[YK\[6%?]"E')]_A,[K8VF%W/G7[>\8^K<Y:DY
MJI!!S%V$=K_<;>C`79/N%]C3X6X%Y?8,2RHZS5+LB'2T!8&[UO/%1NXL_V<P
M.K,[EMAIL PROTECTED];4TEDB'XOB#)+$QP*&5_"K&($L#4/E5F6U6Y75:ER[5>[;
MYM%]VSPZ\W#?_BL(<A9>M(.[.TE(_AZ"Y*6#$.=N*2-1VH,9?VM9/,*;A(V=
[EMAIL PROTECTED]&9EL(-TP2ORP0B:LB-".\,VK]P\,N`$H*H>7`_N+GM:*VQ=
M6FJYF8.)@4Q/QEHN=.=,-GBCM`?&Q",DB\#'N[B9#!P`_;Z")9=.G7:4(*0.
MD,T)5[I;%^=/!VQ*[)@[EMAIL PROTECTED];HUL\!/,HF99)-Z_A#"C\C_
M&/U/2?3)9I>M`.3$A>RHQ_+D?HA+1$=%$4`=G"C&7A]B"]VI%OQP1F_DE'.P
MQ14N=._#(R!;JH!QUM/X,W$71*+PG%'GS+=%1^B?["L'&U\=;QP<3+F-4N&(
<[7K*":/+SOV=,+X>%BBHCD?!_P#?S!_V]#,``.B?
`
end
_______________________________________________
EUGLUG mailing list
[EMAIL PROTECTED]
http://www.euglug.org/mailman/listinfo/euglug

Reply via email to