Handles arguments for small scripts that need to: - read some command line options - read some command line positional arguments - iterate over all lines of some files given on the command line, or stdin if none given - give usage message if positional arguments are missing - give usage message if input files are missing and stdin is not redirected
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 142 143 144 145 146 147 148 | # -*- coding: iso-8859-1 -*-
"""
Handling of arguments: options, arguments, file(s) content iterator
For small scripts that:
- read some command line options
- read some command line positional arguments
- iterate over all lines of some files given on the command line, or stdin if none given
- give usage message if positional arguments are missing
- give usage message if input files are missing and stdin is not redirected
"""
__author__ = 'Peter Kleiweg'
__version__ = '0.2'
__date__ = '2004/08/28'
import os, sys, getopt
class Args:
"""
Perform common tasks on command line arguments
Instance data:
progname (string) -- name of program
opt (dictionary) -- options with values
infile (string) -- name of current file being processed
lineno (int) -- line number of last line read in current file
linesum (int) -- total of lines read
"""
def __init__(self, usage='Usage: %(progname)s [opt...] [file...]'):
"init, usage string: embed program name as %(progname)s"
self.progname = os.path.basename(sys.argv[0])
self.opt = {}
self.infile = None
self.lineno = 0
self.linesum = 0
self._argv = sys.argv[1:]
self._usage = usage
def __iter__(self):
"iterator: set-up"
if self._argv:
self.infile = self._argv.pop(0)
self._in = open(self.infile, 'r')
self._stdin = False
else:
if sys.stdin.isatty():
self.usage() # Doesn't return
self.infile = '<stdin>'
self._in = sys.stdin
self._stdin = True
return self
def next(self):
"iterator: get next line, possibly from next file"
while True:
line = self._in.readline()
if line:
self.lineno += 1
self.linesum += 1
return line
if self._stdin:
break
self._in.close()
try:
self.infile = self._argv.pop(0)
except IndexError:
break
self.lineno = 0
self._in = open(self.infile, 'r')
self.lineno = -1
self.infile = None
raise StopIteration
def getopt(self, shortopts, longopts=[]):
"get options and merge into dict 'opt'"
try:
options, self._argv = getopt.getopt(self._argv, shortopts, longopts)
except getopt.GetoptError:
self.usage()
self.opt.update(dict(options))
def shift(self):
"pop first of remaining arguments (shift)"
try:
return self._argv.pop(0)
except IndexError:
self.usage()
def pop(self):
"pop last of remaining arguments"
try:
return self._argv.pop()
except IndexError:
self.usage()
def warning(self, text):
"print warning message to stderr, possibly with filename and lineno"
if self.lineno > 0:
print >> sys.stderr, '%s:%i: warning: %s' % (self.infile, self.lineno, text)
else:
print >> sys.stderr, '\nWarning %s: %s\n' % (self.progname, text)
def error(self, text):
"print error message to stderr, possibly with filename and lineno, and exit"
if self.lineno > 0:
print >> sys.stderr, '%s:%i: %s' % (self.infile, self.lineno, text)
else:
print >> sys.stderr, '\nError %s: %s\n' % (self.progname, text)
sys.exit(1)
def usage(self):
"print usage message, and exit"
print >> sys.stderr
print >> sys.stderr, self._usage % {'progname': self.progname}
print >> sys.stderr
sys.exit(1)
if __name__ == '__main__':
a = Args('Usage: %(progname)s [-a value] [-b value] [-c] word [file...]')
a.opt['-a'] = 'option a' # set some default option values
a.opt['-b'] = 'option b' #
a.getopt('a:b:c') # get user supplied option values
word = a.shift() # get the first of the remaining arguments
# use a.pop() to get the last instead
for line in a: # iterate over the contents of all remaining arguments (file names)
if a.lineno == 1:
print 'starting new file:', a.infile
a.warning(line.rstrip())
print 'Options:', a.opt
print 'Word:', word
print 'Total number of lines:', a.linesum
print 'Command line:', sys.argv # unchanged
a.warning('warn 1') # print a warning
a.error('error') # print an error message and exit
a.warning('warn 2') # this won't show
|
Migrating from Perl to Python, I wanted an equivalent for this type of Perl code: <pre> while (<>) { process($_); } </pre> This calls process() on all lines of all files given on the command line, or all lines from stdin if no files are given.
I wrote class Args that supplies the equivalent in Python as: <pre> a = Args() for line in a: process(line) </pre> Then I augmenten class Args to have it take care of all tasks I usually perform on command line arguments, and some more.
Version 0.2 is a partial rewrite, following some style suggestion made by Scott David Daniels.