2008-12-12 15 views
11

Cuál es la forma "más limpia" para implementar una línea de comandos de interfaz de usuario, similar a git de, por ejemplo:Implementando un estilo de interfaz de línea de comando "[comando] [acción] [parámetro]"?

git push origin/master 
git remote add origin git://example.com master 

Lo ideal sería que también permite el análisis más flexible, por ejemplo,

jump_to_folder app theappname v2 
jump_to_folder app theappname source 
jump_to_folder app theappname source v2 
jump_to_folder app theappname build v1 
jump_to_folder app theappname build 1 
jump_to_folder app theappname v2 build 

jump_to_folder es el nombre de las secuencias de comandos, app es el comando, theappname es un parámetro de "ubicación fija", "compilación" y "v2" etc. son argumentos (por ejemplo, los posibles argumentos serían cualquier número/cualquier número con prefijo av, o compilación/fuente/tmp/config)

Podría analizar manualmente los argumentos con una serie de if/else/elifs, pero debe haber una forma más elegante de hacerlo.

A modo de ejemplo puramente teóricas, que podría describir el esquema de interfaz de usuario ..

app: 
    fixed: application_name 

    optional params: 
     arg subsection: 
      "build" 
      "source" 
      "tmp" 
      "config" 

     arg version: 
      integer 
      "v" + integer 

continuación, analizar los argumentos proporcionados aunque el esquema anterior, y obtener un diccionario:

>>> print schema.parse(["app", "theappname", "v1", "source"]) 
{ 
    "application_name": "theappname", 
    "params":{ 
     "subsection": "source", 
     "version":"v1" 
    } 
} 

¿El tal sistema existe? Si no, ¿cómo podría implementar algo en esta línea?

Respuesta

10

argparse es perfecto para esto, específicamente "sub-commands" y argumentos posicionales

import argparse 


def main(): 
    arger = argparse.ArgumentParser() 

    # Arguments for top-level, e.g "subcmds.py -v" 
    arger.add_argument("-v", "--verbose", action="count", default=0) 

    subparsers = arger.add_subparsers(dest="command") 

    # Make parser for "subcmds.py info ..." 
    info_parser = subparsers.add_parser("info") 
    info_parser.add_argument("-m", "--moo", dest="moo") 

    # Make parser for "subcmds.py create ..." 
    create_parser = subparsers.add_parser("create") 
    create_parser.add_argument("name") 
    create_parser.add_argument("additional", nargs="*") 

    # Parse 
    opts = arger.parse_args() 

    # Print option object for debug 
    print opts 

    if opts.command == "info": 
     print "Info command" 
     print "--moo was %s" % opts.moo 

    elif opts.command == "create": 
     print "Creating %s" % opts.name 
     print "Additional: %s" % opts.additional 

    else: 
     # argparse will error on unexpected commands, but 
     # in case we mistype one of the elif statements... 
     raise ValueError("Unhandled command %s" % opts.command) 


if __name__ == '__main__': 
    main() 

Esto puede ser usado de esta manera:

$ python subcmds.py create myapp v1 blah 
Namespace(additional=['v1', 'blah'], command='create', name='myapp', verbose=0) 
Creating myapp 
Additional: ['v1', 'blah'] 
$ python subcmds.py info --moo 
usage: subcmds.py info [-h] [-m MOO] 
subcmds.py info: error: argument -m/--moo: expected one argument 
$ python subcmds.py info --moo 1 
Namespace(command='info', moo='1', verbose=0) 
Info command 
--moo was 1 
+0

¡Agradable! ¡Eso es exactamente lo que ordenó el doctor! ¡Gracias! –

+0

buen ejemplo de cómo usar los subparsores. ¡Gracias! – jozefow

2

recta de uno de mis guiones:

import sys 

def prog1_func1_act1(): print "pfa1" 
def prog2_func2_act2(): print "pfa2" 

commands = { 
    "prog1 func1 act1": prog1_func1_act1, 
    "prog2 func2 act2": prog2_func2_act2 
} 

try: 
    commands[" ".join(sys.argv[1:])]() 
except KeyError: 
    print "Usage: ", commands.keys() 

Es una solución muy rápida y sucia, pero funciona muy bien para mi uso. Si tuviera que limpiarlo un poco, probablemente agregaría argparse a la mezcla para analizar los argumentos posicionales y de palabra clave.

+0

El problema con esto se arreglan los argumentos ... Por ejemplo, no podría tener fácilmente una opción "v001" .. "v102", salvo crear una clave para cada combinación .. – dbr

+0

Sí, lo sé, y es por eso que mencioné argparse. Por ejemplo, si tiene relativamente pocos "comandos" y "acciones", pero muchos parámetros, puede enviar a acciones específicas con el enfoque que uso, y luego pasar parámetros (y argumentos opcionales de palabras clave) a su secuencia de comandos que usa argparse . – kmelva

+0

plus, puede agregar un meta-comando 'help' que imprime el contenido de los comandos y muestra sus .__ doc__ strings – pjz

1

Python tiene un módulo para analizar las opciones de línea de comandos, optparse.

+0

Desafortunadamente analiza las opciones * de la línea de comandos, no * argumentos * como dbr pregunta –

+0

Exactamente. Es el más adecuado para los argumentos del tipo "-f 2 -v -z 55", hasta donde yo sé, a menos que me falta algo. – dbr

9

El módulo cmd probablemente funcionaría bien para esto.

Ejemplo:

import cmd 

class Calc(cmd.Cmd): 
    def do_add(self, arg): 
     print sum(map(int, arg.split())) 

if __name__ == '__main__': 
    Calc().cmdloop() 

Run que:

$python calc.py 
(Cmd) add 4 5 
9 
(Cmd) help 

Undocumented commands: 
====================== 
add help 

(Cmd) 

Ver la Python docs o PyMOTW site para obtener más información.

0

Aquí está mi sugerencia.

  1. Cambie su gramática levemente.

  2. Utilice optparse.

Lo ideal sería que también permite el análisis más flexible, por ejemplo,

jump_to_folder -n theappname -v2 cmd 
jump_to_folder -n theappname cmd source 
jump_to_folder -n theappname -v2 cmd source 
jump_to_folder -n theappname -v1 cmd build 
jump_to_folder -n theappname -1 cmd build 
jump_to_folder -n theappname -v2 cmd build 

entonces usted tiene 1 o 2 argumentos: el comando es siempre el primer argumento. Su argumento opcional es siempre el segundo arg.

Todo lo demás son opciones, sin ningún orden en particular.

Cuestiones relacionadas