开发者

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 )
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜