getopt() not enforcing required arguments?
I'm having problems with this getopt()
code in a script that I'm writing which does some simple file manipulation given 2 required parameters (input filename and output filename) and/or 2 optional/situational arguments (debug or help).
Code is:
def m开发者_如何学编程ain(argv):
try:
opts, args = getopt.getopt(argv, "i:o:dh", ["input-file=", "output-file=", "debug", "help"])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-h", "--help"):
usage()
sys.exit()
elif opt in ("-d", "--debug"):
global _debug
_debug = 1
elif opt in ("-i", "--input-file"):
u_input_file_name = arg
elif opt in ("-o", "--output-file"):
u_output_file_name = arg
According to the getopt()
documentation:
options that require an argument followed by a colon ('
:
'; i.e., the same format that Unix getopt() uses).
The problem is that as I understand it, the variables/args followed by a :
should be enforced as required ... but the options i
and o
are not being enforced. Running this snippet garners an error about u_input_file_name
being referenced before being assigned:
[tdelane@fbsd81-1 ~/python]$ ./inco_add_cm_mpscli.py -o google
Traceback (most recent call last):
File "./inco_add_cm_mpscli.py", line 57, in <module>
main(sys.argv[1:])
File "./inco_add_cm_mpscli.py", line 25, in main
infile = open(u_input_file_name, 'r')
UnboundLocalError: local variable 'u_input_file_name' referenced before assignment
What am I doing wrong?
An option followed by a colon only means that it needs an argument. It doesn't mean that the option is enforced. You should write your own code to enforce the existence of options/arguments.
Just as a note, I found that argparse is simpler and more useful than getopt, and it support required arguments.
http://docs.python.org/2/howto/argparse.html#id1
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
Command Line
$ python prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
In case this is useful to anyone. Here is a boiler plate that I use for creating a python script with command line options. It handles required options. If a required option is not specified, then the script will terminate with an error.
#!/usr/bin/python
import os
import sys
import getopt
import logging
# This will get the name of this file
script_name = os.path.basename(__file__)
default_loglevel = 'info'
##
# @brief Help document for this script. See the main function below.
#
help = f'''
{script_name} -c com_port [-o output_file] [--loglevel level]
Reads the temperature data from a radio. The temperature data is output in csv form.
examples:
Read table from radio attached to com4 and write the table to the file
output.csv.
{script_name} -c com4 -o output.csv
Read table from radio attached to com3 and write the table to stdout.
You can use IO redirection to send the contents where every you want.
# just print to the terminal
{script_name} -c com3
# redirect to another file
{script_name} -c com3 > somefile.csv
# filter out temperatures that are -100
{script_name} -c com3 | grep -v '^-100'
-c com_port
--com_port comport
Name of the COM port attached to the radio
-o output_file
--output output_file
If specified write the table data to the given file. If not specified
the data will be written to stdout.
--loglevel critical | error | warning | info | debug | notset
Control the verbosity of the script by setting the log level. Critical
is the least verbose and notset is the most verbose.
The default loglevel is {default_loglevel}.
These values correspond directly to the python logging module levels.
(i.e. https://docs.python.org/3/howto/logging.html#logging-levels)
-h
--help
print this message
'''
def print_help():
print(help, file=sys.stderr)
class RequiredOptions:
'''Just something to keep track of required options'''
def __init__(self, options=[]):
self.required_options = options
def add(self, option):
if option not in self.required_options:
self.required_options.append(option)
def resolve(self, option):
if option in self.required_options:
self.required_options.remove(option)
def optionsResolved(self):
if len(self.required_options):
return False
else:
return True
def main(argv):
# Use the logging module to print non table data. These prints will be sent
# to stderr. The verbosity of the script can by adjusted via the setLevel
# method.
#
logging.getLogger().setLevel(default_loglevel.upper())
try:
opts, args = getopt.getopt(argv,"hc:o:",["help", "com_port=", "output=","loglevel="])
except getopt.GetoptError as e:
print_help()
logging.exception(e)
sys.exit(2)
# This can be overridden with the --output option.
#
output_file = sys.stdout
# As the required options are encountered they are removed from this list.
# After all of the args have been processed, require_options should be
# empty.
#
required_options = RequiredOptions([ 'com_port' ])
for opt, arg in opts:
if opt in ('-h', '--help'):
print_help()
sys.exit(0)
elif opt in ("-o", "--output"):
output_file = open(arg, 'w')
elif opt in ("-c", "--com_port"):
com_port = arg
required_options.resolve('com_port')
elif opt in ("--loglevel"):
# Convert to uppercase
#
loglevel = arg.upper()
logging.getLogger().setLevel(loglevel)
else:
print_help()
# Verify that all of the required options have been specified
#
if not required_options.optionsResolved():
print_help()
logging.error("The following required options were not specified:" + ' '.join(required_options.required_options))
# indicate that there was an error by returning a non-zero value.
#
sys.exit(1)
# Now do your work
logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')
logging.error('error message')
logging.critical('critical message')
if __name__ == "__main__":
main(sys.argv[1:])
I would just create global variable like argbit and use bitwise operation instead of flags for each arg. I used like:
argbit=1
for each arg loop:
case arg1: #mandatory
argbit <<= 1
do stuff and break
case arg2: #optional
do stuff and break
now based on your args it will be left shifted so at end just check its value
if argbit != value:
usage_and_exit()
if you have two mandatory args its value will be 4 like 2 ^ n.
Ran into the same problem, here's how I solved it
try:
required_argument1 # If argument is missing, it will error, we
# catch that error below in -except
required_argument2
required_argument3
except Error as e: # Whatever your error is (mine was a keyError)
print( 'argument ' + str(e) + is missing )
精彩评论