2010-11-05 25 views
24

¿Es posible acceder a varios valores para una opción usando getopt o optparse, como se muestra en el siguiente ejemplo:¿Procesar valores múltiples para una sola opción usando getopt/optparse?

./hello_world -c arg1 arg2 arg3 -b arg4 arg5 arg6 arg7 

Tenga en cuenta que el número de valores reales para cada opción (-c, -b) podría ya sea 1 o 100. No quiero usar: ./hello_world -c "arg1 arg2 arg3" -b "arg4 arg5 arg6 arg7"

Me parece que esto puede no ser posible (y tal vez en violación de POSIX), por favor corríjanme si me equivoco.

He visto ejemplos donde se pueden recopilar todas las no opciones al final de la línea (./hello_world -c arg1 -b arg1 arg2 arg3) ... pero no para la primera opción múltiple.

Me gustaría que mi aplicación funcione en una amplia gama de plataformas con diferentes versiones de Python, por lo que no he examinado argparser.

Respuesta

5

Puede hacer esto con el parámetro nargs en argparse que viene con Python2.7, y puede descargar here.

Creo que es una de las mejoras añadidas a argparse que no está en optparse. Por lo tanto, desafortunadamente, no creo que haya una buena forma de manejar esto con optparse o getopt (que es aún más antiguo).

Una solución rápida y sucia puede ser renunciar a optparse/getop/argparse y simplemente analizar sys.argv usted mismo.

O, yendo en la dirección opuesta, es posible considerar el envasado de una copia inmovilizada de argparse (~ 88K) (algo rebautizada como argparse_static) con su programa, y ​​ la importación de esta manera:

try: 
    import argparse 
except ImportError: 
    import argparse_static as argparse 

Eso De esta forma, el programa usará argparse si está instalado, y usará argparse_static si no lo está. Lo mejor de todo es que no tendrá que volver a escribir mucho código ya que argparse pasa a ser estándar.

+0

Muchas gracias por dos respuestas rápidas y en profundidad. Me las arreglé para escribir mi propio analizador de opciones que maneja las cosas. Descargar una copia estática de argparse no fue algo que haya considerado, pero ahora está sobre la mesa. –

+0

Mi voto es con argparse. Es más fácil entender que Optparse. (Y fui uno de los pirateadores de optparse cuando era Optik, antes de que llegara a la biblioteca estándar) –

5

Ni getopt ni optparse lo admiten de fábrica. Además, en el modo predeterminado (GNU), los argumentos adicionales se tratarán como args intercalados, es decir, estarán disponibles como argumentos restantes al final del procesamiento.

La convención sería requerir repetida mención del mismo argumento, es decir

./hello_world -c arg1 -c arg2 -c arg3 -b arg4 -b arg5 -b arg6 -b arg7 

esta es la voluntad compatible.

Si desea que todo funcione de la manera que especifique (es decir, tanto -b como -c extender hasta el siguiente argumento o el final de la lista de argumentos), entonces puede hackear algo en conjunto basado en optparse. Heredar de OptionParser y anular _process_short_opts. Si es una de tus opciones, trátala en la subclase, de lo contrario, reenvía a la clase base.

+1

Gracias por explicarnos la convención. Eso fue lo que pensé ... El problema es que el número de valores para cada opción hace que el uso de esa convención no sea fácil de usar. Creo que es probable que termine iterando a través de sys.argv y manejando cosas por mi cuenta. –

14

Sí, se puede hacer con optparse.

Este es un ejemplo:

./test.py --categories=aaa --categories=bbb --categories ccc arg1 arg2 arg3 

que imprime:

arguments: ['arg1', 'arg2', 'arg3'] 
options: {'categories': ['aaa', 'bbb', 'ccc']} 

continuación ejemplo de trabajo completo:

#!/usr/bin/env python 

import os, sys 
from optparse import OptionParser 
from optparse import Option, OptionValueError 

VERSION = '0.9.4' 

class MultipleOption(Option): 
    ACTIONS = Option.ACTIONS + ("extend",) 
    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) 
    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) 
    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",) 

    def take_action(self, action, dest, opt, value, values, parser): 
     if action == "extend": 
      values.ensure_value(dest, []).append(value) 
     else: 
      Option.take_action(self, action, dest, opt, value, values, parser) 


def main(): 
    PROG = os.path.basename(os.path.splitext(__file__)[0]) 
    long_commands = ('categories') 
    short_commands = {'cat':'categories'} 
    description = """Just a test""" 
    parser = OptionParser(option_class=MultipleOption, 
          usage='usage: %prog [OPTIONS] COMMAND [BLOG_FILE]', 
          version='%s %s' % (PROG, VERSION), 
          description=description) 
    parser.add_option('-c', '--categories', 
         action="extend", type="string", 
         dest='categories', 
         metavar='CATEGORIES', 
         help='comma separated list of post categories') 

    if len(sys.argv) == 1: 
     parser.parse_args(['--help']) 

    OPTIONS, args = parser.parse_args() 
    print "arguments:", args 
    print "options:", OPTIONS 

if __name__ == '__main__': 
    main() 

Más información en http://docs.python.org/library/optparse.html#adding-new-actions

4

Otra opción sería definir un separador y procesarlo localmente, como las opciones en el comando mount.

Por ejemplo, si , se puede utilizar como un separador:

... 
args, _ = getopt.getopt(sys.argv[1:],'b:') 
for flag, arg in args: 
    if flag=='-b': all_arguments = arg.split(',') 
... 

$ ./test -b opt1,opt2,opt3 

Lo mismo para el espacio! Pero luego sus usuarios deben citarlo correctamente.

$ ./test -b 'opt1 opt2 opt3' 
7

Disculpa venir tarde a la fiesta pero acabo de resolver esto con optparse usando la bandera de nargs.

parser.add_option('-c','--categories', dest='Categories', nargs=4) 

http://docs.python.org/2/library/optparse.html#optparse.Option.nargs

También es digno de mención, que argparse (sugerido por unutbu) es ahora parte de la distribución estándar de Python, mientras optparse está en desuso.

+0

Supongamos que si quisiera verificarlo aquí, si el usuario no ha ingresado los argumentos. cómo leer 4 argumentos en "option.Categories" uno por uno – kzs

+1

Tenga en cuenta que esto le limita a 4 argumentos para '-c'. – Plasma

+0

A partir de @Plasma, también requiere 4 argumentos, no más y no menos. – sijpkes

8

A pesar de los reclamos de los otros comentarios, esto es posible con optparse de vanilla, al menos a partir de python 2.7. Solo necesita usar action = "append". Desde el docs:

parser.add_option("-t", "--tracks", action="append", type="int") 

Si -T3 se ve en la línea de comandos, optparse hace el equivalente de:

options.tracks = [] 
options.tracks.append(int("3")) 

Si, un poco más adelante, --tracks = 4 se ve, que hace:

options.tracks.append(int("4")) 
+1

Esta es una solución más convencional al problema. Pasar múltiples argumentos a un solo indicador es una práctica engañosa y tiene una alta probabilidad de causar problemas en el futuro. Si necesita palabras IFS separadas pasadas a * opción *, animo a los usuarios a citar el valor y luego dividirlo dentro de su código. De lo contrario, quédese con los argumentos de comando o indicadores repetidos como se sugiere en esta respuesta. – Ben

3

una más fácil:

make_option(
    "-c", 
    "--city", 
    dest="cities", 
    action="append", 
    default=[], 
    help="specify cities", 
) 

Append action es la solución más fácil para este problema.

Cuestiones relacionadas