lista ejecutar desde consola comandos python command-line command-line-arguments command-line-parsing

ejecutar - Pasar una lista a Python desde la línea de comandos



lista comandos python (4)

Me gustaría hacer que mi script python se ejecute desde la línea de comando cuando se suministra con algunos argumentos. Sin embargo, uno de los argumentos debería ser una lista de opciones específicas para un segmento del script. ¿Sería el análisis sintáctico de cadenas la única forma de hacerlo al construir realmente la lista después de que la cadena de "línea de comandos" se divida desde comas? Si es así, ¿cómo harías para eso?

Ejemplo: -details = [''nombre'', ''título'', ''dirección'']


Sí, argparse es su mejor opción , y si desea proporcionar una lista de valores a uno de sus argumentos nombrados, se ve así (el parámetro nargs es la clave para esto):

>>> import argparse >>> arg_parser = argparse.ArgumentParser() >>> arg_parser.add_argument(''--details'', nargs=''*'', type=str, default=[], help=''a list of the details'') # your args on the command line like this example >>> the_args = arg_parser.parse_args("--details ''name'' ''title'' ''address''".split()) >>> print the_args.details ["''name''", "''title''", "''address''"])


Programa:

import sys, ast, getopt, types def main(argv): arg_dict={} switches={''li'':list,''di'':dict,''tu'':tuple} singles=''''.join([x[0]+'':'' for x in switches]) long_form=[x+''='' for x in switches] d={x[0]+'':'':''--''+x for x in switches} try: opts, args = getopt.getopt(argv, singles, long_form) except getopt.GetoptError: print "bad arg" sys.exit(2) for opt, arg in opts: if opt[1]+'':'' in d: o=d[opt[1]+'':''][2:] elif opt in d.values(): o=opt[2:] else: o ='''' print opt, arg,o if o and arg: arg_dict[o]=ast.literal_eval(arg) if not o or not isinstance(arg_dict[o], switches[o]): print opt, arg, " Error: bad arg" sys.exit(2) for e in arg_dict: print e, arg_dict[e], type(arg_dict[e]) if __name__ == ''__main__'': main(sys.argv[1:])

Línea de comando:

python py.py --l=''[1,2,3,[1,2,3]]'' -d "{1:''one'',2:''two'',3:''three''}" --tu=''(1,2,3)''

Salida:

args: [''--l=[1,2,3,[1,2,3]]'', ''-d'', "{1:''one'',2:''two'',3:''three''}", ''--tu=(1,2,3)''] tu (1, 2, 3) <type ''tuple''> di {1: ''one'', 2: ''two'', 3: ''three''} <type ''dict''> li [1, 2, 3, [1, 2, 3]] <type ''list''>

Este fragmento de código tomará interruptores de comando cortos o largos como -l o --li= y analizará el texto después del cambio en una estructura de datos de Python como una lista, tupla o un dict. La estructura de datos analizados termina en un diccionario con la tecla de cambio de forma larga.

Usar ast.literal_eval es relativamente seguro. Solo puede analizar las definiciones de datos de Python.


argparse es bueno para esto, está en la biblioteca estándar a partir de 2.7 y 3.2, pero por lo demás es una pip install .

Su principal preocupación al especificar una lista de longitud variable puede abordarse haciendo que la lista se interprete como un único argumento en el shell mediante el uso de comillas (podría depender de su shell, supongo):

% python prog.py ''name title address'' spam

donde prog.py contiene

import sys my_list = sys.argv[1].split() # my_list is [''name'', ''title'', ''address''] if ''name'' in my_list: do_something()

o similar. Use un argumento con división para delimitar su lista:

% python prog.py "you''re a foo, lift the bar" my_list = [x.strip() for x in sys.argv[1].split('','')] # my_list is ["you''re a foo", "lift the bar"]

Pero por favor use argparse en su lugar; especialmente si quiere usar las banderas de estilo -c .

Una forma de interpretar tu pregunta es:

"Ya estoy usando argparse, ya que esa es la forma sensata de interpretar los argumentos de línea de comando en Python. ¿Cómo especifico que algunas opciones están dentro de una categoría específica?"

En tu pregunta, has mostrado un ejemplo de algo que las conchas que uso se ahogarían;

% python prog.py -v -details=[''name'', ''title'', ''address''] --quickly -t 4

no haría analizar a Python porque usaría espacios para separar argumentos y podría usar [y] como sintaxis de shell.

Sugiero lo siguiente en su lugar

% python prog.py -v --details name title address --quickly -t 4

donde un archivo prog.py de

import argparse parser = argparse.ArgumentParser() parser.add_argument(''-v'', action=''store_true'') parser.add_argument(''--details'', nargs=''*'') parser.add_argument(''--quickly'', action=''store_true'') parser.add_argument(''-t'') args = parser.parse_args() #args is Namespace(details=[''asdf'', ''a'', ''a''], quickly=False, t=''4'', v=True) details = args.details #details is [''asdf'', ''a'', ''a'']

Ahora, según tu pregunta, no tienes que hacer la cadena de análisis tú mismo.


Realmente me gusta el enfoque del lobo usando colecciones de longitud variable como argumentos de cadena explícitos.

En mi opinión, nargs=''*'' tiene desventajas notables: cuando intentes coleccionar cadenas como argumentos posicionales (al menos una cadena debe estar presente), o si estás tratando de usar subparsers, encontrarás que nargs=''*'' y nargs=''+'' usan una terminación nargs=''+'' , y no parecen dejar de consumir por alguna buena razón. Incluso si aparece la sintaxis para un argumento opcional o un número, el tipo de cadena () seguirá consumiendo. (Esto se vuelve más difícil de anticipar con los subparsers).

En el mejor de los casos, los argumentos colocados después (posicional y opcional) se ignoran, y lo que es peor, es probable que pase tipos de datos corruptos a la matriz argparse.

Deberíamos poder definir un tipo de acción personalizado que esté buscando una cadena entrecomillada. Si encuentra uno, entonces adaptamos los ejemplos del lobo (casi al pie de la letra parece).

Esto mantiene las cosas limpias en argparse y hace que el uso general de las colecciones variables sea mucho menos pernicioso.