Question

Quel est le le plus simple, laconique, et plus flexible méthode ou bibliothèque pour analyser les arguments de ligne de commande Python ?

Était-ce utile?

La solution

Cette réponse suggère optparse ce qui convient aux anciennes versions de Python.Pour Python 2.7 et supérieur, argparse remplace optparse.Voir cette réponse pour plus d'informations.

Comme d'autres personnes l'ont souligné, il vaut mieux utiliser optparse plutôt que getopt.getopt est à peu près un mappage un à un des fonctions standard de la bibliothèque getopt(3) C, et pas très facile à utiliser.

optparse, tout en étant un peu plus verbeux, est bien mieux structuré et plus simple à étendre ultérieurement.

Voici une ligne typique pour ajouter une option à votre analyseur :

parser.add_option('-q', '--query',
            action="store", dest="query",
            help="query string", default="spam")

Cela parle de lui-même ;au moment du traitement, il acceptera -q ou --query comme options, stockera l'argument dans un attribut appelé query et aura une valeur par défaut si vous ne la spécifiez pas.Il est également auto-documenté dans la mesure où vous déclarez l'argument help (qui sera utilisé lors de l'exécution avec -h/--help) ici même avec l'option.

Habituellement, vous analysez vos arguments avec :

options, args = parser.parse_args()

Cela analysera, par défaut, les arguments standard passés au script (sys.argv[1:])

options.query sera alors défini sur la valeur que vous avez transmise au script.

Vous créez un analyseur simplement en faisant

parser = optparse.OptionParser()

Ce sont toutes les bases dont vous avez besoin.Voici un script Python complet qui montre ceci :

import optparse

parser = optparse.OptionParser()

parser.add_option('-q', '--query',
    action="store", dest="query",
    help="query string", default="spam")

options, args = parser.parse_args()

print 'Query string:', options.query

5 lignes de python qui vous montrent les bases.

Enregistrez-le dans sample.py et exécutez-le une fois avec

python sample.py

et une fois avec

python sample.py --query myquery

Au-delà de cela, vous constaterez qu’optparse est très facile à étendre.Dans l'un de mes projets, j'ai créé une classe Command qui vous permet d'imbriquer facilement des sous-commandes dans une arborescence de commandes.Il utilise beaucoup optparse pour enchaîner les commandes.Ce n'est pas quelque chose que je peux facilement expliquer en quelques lignes, mais n'hésitez pas à parcourir mon référentiel pour la classe principale, ainsi que une classe qui l'utilise et l'analyseur d'options

Autres conseils

D'autres réponses mentionnent que argparse est la voie à suivre pour le nouveau Python, mais ne donne pas d'exemples d'utilisation.Pour être complet, voici un bref résumé de la façon d'utiliser argparse :

1) Initialiser

import argparse

# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')

2) Ajouter des arguments

# Required positional argument
parser.add_argument('pos_arg', type=int,
                    help='A required integer positional argument')

# Optional positional argument
parser.add_argument('opt_pos_arg', type=int, nargs='?',
                    help='An optional integer positional argument')

# Optional argument
parser.add_argument('--opt_arg', type=int,
                    help='An optional integer argument')

# Switch
parser.add_argument('--switch', action='store_true',
                    help='A boolean switch')

3) Analyser

args = parser.parse_args()

4) Accès

print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)

5) Vérifier les valeurs

if args.pos_arg > 10:
    parser.error("pos_arg cannot be larger than 10")

Usage

Utilisation correcte :

$ ./app 1 2 --opt_arg 3 --switch

Argument values:
1
2
3
True

Arguments incorrects :

$ ./app foo 2 --opt_arg 3 --switch
usage: convert [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
app: error: argument pos_arg: invalid int value: 'foo'

$ ./app 11 2 --opt_arg 3
Argument values:
11
2
3
False
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
convert: error: pos_arg cannot be larger than 10

Aide complète :

$ ./app -h

usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]

Optional app description

positional arguments:
  pos_arg            A required integer positional argument
  opt_pos_arg        An optional integer positional argument

optional arguments:
  -h, --help         show this help message and exit
  --opt_arg OPT_ARG  An optional integer argument
  --switch           A boolean switch

Utiliser Docopt

Depuis 2012, Python dispose d'un outil très simple, puissant et vraiment cool module d'analyse des arguments appelé docopt.Il fonctionne avec Python 2.6 à 3.5 et ne nécessite aucune installation (il suffit de le copier).Voici un exemple tiré de sa documentation :

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)

Alors c'est ça:2 lignes de code plus votre chaîne doc qui est essentiel et vous obtenez vos arguments analysés et disponibles dans votre objet arguments.Je t'ai dit que c'était cool, n'est-ce pas ;-)

Utiliser python-fire

Depuis 2017 python-feu a un autre module sympa qui peut donner une interface CLI à votre code avec vous zéro analyse des arguments.Voici un exemple simple tiré de la documentation (ce petit programme expose la fonction double à la ligne de commande):

import fire

class Calculator(object):

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

Depuis la ligne de commande, vous pouvez exécuter :

> calculator.py double 10
20
> calculator.py double --number=15
30

Génial, n'est-ce pas ?

La nouvelle manière branchée est argparse pour ces les raisons.argparse > optparse > getopt

mise à jour: Depuis py2.7 argparse fait partie de la bibliothèque standard et optparse est obsolète.

je préfère Cliquez sur.Il résume les options de gestion et permet de "(...) créer de belles interfaces de ligne de commande de manière composable avec aussi peu de code que nécessaire".

Voici un exemple d'utilisation :

import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
              help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()

Il génère également automatiquement des pages d'aide bien formatées :

$ python hello.py --help
Usage: hello.py [OPTIONS]

  Simple program that greets NAME for a total of COUNT times.

Options:
  --count INTEGER  Number of greetings.
  --name TEXT      The person to greet.
  --help           Show this message and exit.

Presque tout le monde utilise optez pour

Voici l'exemple de code pour la doc :

import getopt, sys

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)
    output = None
    verbose = False
    for o, a in opts:
        if o == "-v":
            verbose = True
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-o", "--output"):
            output = a

En un mot, voici comment cela fonctionne.

Vous avez deux types d'options.Ceux qui reçoivent des arguments et ceux qui sont comme des commutateurs.

sys.argv c'est à peu près ton char** argv en C.Comme en C, vous sautez le premier élément qui est le nom de votre programme et analysez uniquement les arguments : sys.argv[1:]

Getopt.getopt l'analysera selon la règle que vous donnez en argument.

"ho:v" décrit ici les courts arguments : -ONELETTER.Le : signifie que -o accepte un argument.

Enfin ["help", "output="] décrit de longs arguments ( --MORETHANONELETTER ).Le = après la sortie, cela signifie à nouveau que la sortie accepte un argument.

Le résultat est une liste de couples (option,argument)

Si une option n'accepte aucun argument (comme --help ici le arg part est une chaîne vide.Vous souhaitez ensuite généralement parcourir cette liste et tester le nom de l'option comme dans l'exemple.

J'espère que cela vous a aidé.

Utiliser optparse qui vient avec la bibliothèque standard.Par exemple:

#!/usr/bin/env python
import optparse

def main():
  p = optparse.OptionParser()
  p.add_option('--person', '-p', default="world")
  options, arguments = p.parse_args()
  print 'Hello %s' % options.person

if __name__ == '__main__':
  main()

Source: Utiliser Python pour créer des outils de ligne de commande UNIX

Cependant, depuis Python 2.7, optparse est obsolète, voir : Pourquoi utiliser argparse plutôt que optparse ?

Juste au cas où vous en auriez besoin, cela peut vous aider si vous en avez besoin saisir Arguments Unicode sur Win32 (2K, XP etc):


from ctypes import *

def wmain(argc, argv):
    print argc
    for i in argv:
        print i
    return 0

def startup():
    size = c_int()
    ptr = windll.shell32.CommandLineToArgvW(windll.kernel32.GetCommandLineW(), byref(size))
    ref = c_wchar_p * size.value
    raw = ref.from_address(ptr)
    args = [arg for arg in raw]
    windll.kernel32.LocalFree(ptr)
    exit(wmain(len(args), args))
startup()

Valeurs par défaut des arguments de ligne de commande légers

Bien que argparse est génial et constitue la bonne réponse pour les commutateurs de ligne de commande entièrement documentés et les fonctionnalités avancées, vous pouvez utiliser les valeurs par défaut des arguments de fonction pour gérer très simplement les arguments de position simples.

import sys

def get_args(name='default', first='a', second=2):
    return first, int(second)

first, second = get_args(*sys.argv)
print first, second

L'argument 'name' capture le nom du script et n'est pas utilisé.Le résultat du test ressemble à ceci :

> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20

Pour les scripts simples où je veux juste quelques valeurs par défaut, je trouve cela tout à fait suffisant.Vous souhaiterez peut-être également inclure une certaine coercition de type dans les valeurs de retour ou les valeurs de ligne de commande seront toutes des chaînes.

Je pense que le meilleur moyen pour les projets plus importants est optparse, mais si vous cherchez un moyen simple, peut-être http://werkzeug.pocoo.org/documentation/script est quelque chose pour vous.

from werkzeug import script

# actions go here
def action_foo(name=""):
    """action foo does foo"""
    pass

def action_bar(id=0, title="default title"):
    """action bar does bar"""
    pass

if __name__ == '__main__':
    script.run()

Donc, fondamentalement, chaque fonction Action_ * est exposée à la ligne de commande et un bon message d'aide est généré gratuitement.

python foo.py 
usage: foo.py <action> [<options>]
       foo.py --help

actions:
  bar:
    action bar does bar

    --id                          integer   0
    --title                       string    default title

  foo:
    action foo does foo

    --name                        string

Je préfère optparse à getopt.C'est très déclaratif :vous lui indiquez les noms des options et les effets qu'elles devraient avoir (par exemple, définir un champ booléen), et il vous rend un dictionnaire rempli selon vos spécifications.

http://docs.python.org/lib/module-optparse.html

consoleargs mérite d'être mentionné ici.Il est très simple à utiliser.Vérifiez-le:

from consoleargs import command

@command
def main(url, name=None):
  """
  :param url: Remote URL 
  :param name: File name
  """
  print """Downloading url '%r' into file '%r'""" % (url, name)

if __name__ == '__main__':
  main()

Maintenant en console :

% python demo.py --help
Usage: demo.py URL [OPTIONS]

URL:    Remote URL 

Options:
    --name -n   File name

% python demo.py http://www.google.com/
Downloading url ''http://www.google.com/'' into file 'None'

% python demo.py http://www.google.com/ --name=index.html
Downloading url ''http://www.google.com/'' into file ''index.html''

Le code Argparse peut être plus long que le code d’implémentation réel !

C'est un problème que je trouve avec les options d'analyse d'arguments les plus populaires, c'est que si vos paramètres ne sont que modestes, le code pour les documenter devient disproportionné par rapport aux avantages qu'ils apportent.

Un nouveau venu sur la scène de l'analyse des arguments (je pense) est place.

Il fait des compromis reconnus avec argparse, mais utilise la documentation en ligne et s'enroule simplement autour de main() tapez la fonction fonction :

def main(excel_file_path: "Path to input training file.",
     excel_sheet_name:"Name of the excel sheet containing training data including columns 'Label' and 'Description'.",
     existing_model_path: "Path to an existing model to refine."=None,
     batch_size_start: "The smallest size of any minibatch."=10.,
     batch_size_stop:  "The largest size of any minibatch."=250.,
     batch_size_step:  "The step for increase in minibatch size."=1.002,
     batch_test_steps: "Flag.  If True, show minibatch steps."=False):
"Train a Spacy (http://spacy.io/) text classification model with gold document and label data until the model nears convergence (LOSS < 0.5)."

    pass # Implementation code goes here!

if __name__ == '__main__':
    import plac; plac.call(main)

Voici une méthode, pas une bibliothèque, qui semble fonctionner pour moi.

Les objectifs ici sont d'être concis, chaque argument analysé par une seule ligne, les arguments s'alignent pour plus de lisibilité, le code est simple et ne dépend d'aucun module spécial (uniquement os + sys), avertit gracieusement des arguments manquants ou inconnus. , utilisez une simple boucle for/range() et fonctionne sur python 2.x et 3.x

Deux indicateurs à bascule (-d, -v) et deux valeurs contrôlées par des arguments (-i xxx et -o xxx) sont affichés.

import os,sys

def HelpAndExit():
    print("<<your help output goes here>>")
    sys.exit(1)

def Fatal(msg):
    sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
    sys.exit(1)

def NextArg(i):
    '''Return the next command line argument (if there is one)'''
    if ((i+1) >= len(sys.argv)):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return(1, sys.argv[i+1])

### MAIN
if __name__=='__main__':

    verbose = 0
    debug   = 0
    infile  = "infile"
    outfile = "outfile"

    # Parse command line
    skip = 0
    for i in range(1, len(sys.argv)):
        if not skip:
            if   sys.argv[i][:2] == "-d": debug ^= 1
            elif sys.argv[i][:2] == "-v": verbose ^= 1
            elif sys.argv[i][:2] == "-i": (skip,infile)  = NextArg(i)
            elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
            elif sys.argv[i][:2] == "-h": HelpAndExit()
            elif sys.argv[i][:1] == "-":  Fatal("'%s' unknown argument" % sys.argv[i])
            else:                         Fatal("'%s' unexpected" % sys.argv[i])
        else: skip = 0

    print("%d,%d,%s,%s" % (debug,verbose,infile,outfile))

Le but de NextArg() est de renvoyer l'argument suivant tout en vérifiant les données manquantes, et 'skip' saute la boucle lorsque NextArg() est utilisé, en gardant l'indicateur analysé à une seule ligne.

J'ai étendu l'approche d'Erco pour permettre les arguments positionnels requis et les arguments facultatifs.Ceux-ci doivent précéder les -d, -v etc.arguments.

Les arguments positionnels et facultatifs peuvent être récupérés respectivement avec PosArg(i) et OptArg(i, default).Lorsqu'un argument facultatif est trouvé, la position de départ de la recherche d'options (par ex.-i) est avancé de 1 pour éviter de provoquer un décès « inattendu ».

import os,sys


def HelpAndExit():
    print("<<your help output goes here>>")
    sys.exit(1)

def Fatal(msg):
    sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
    sys.exit(1)

def NextArg(i):
    '''Return the next command line argument (if there is one)'''
    if ((i+1) >= len(sys.argv)):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return(1, sys.argv[i+1])

def PosArg(i):
    '''Return positional argument'''
    if i >= len(sys.argv):
        Fatal("'%s' expected an argument" % sys.argv[i])
    return sys.argv[i]

def OptArg(i, default):
    '''Return optional argument (if there is one)'''
    if i >= len(sys.argv):
        Fatal("'%s' expected an argument" % sys.argv[i])
    if sys.argv[i][:1] != '-':
        return True, sys.argv[i]
    else:
        return False, default


### MAIN
if __name__=='__main__':

    verbose = 0
    debug   = 0
    infile  = "infile"
    outfile = "outfile"
    options_start = 3

    # --- Parse two positional parameters ---
    n1 = int(PosArg(1))
    n2 = int(PosArg(2))

    # --- Parse an optional parameters ---
    present, a3 = OptArg(3,50)
    n3 = int(a3)
    options_start += int(present)

    # --- Parse rest of command line ---
    skip = 0
    for i in range(options_start, len(sys.argv)):
        if not skip:
            if   sys.argv[i][:2] == "-d": debug ^= 1
            elif sys.argv[i][:2] == "-v": verbose ^= 1
            elif sys.argv[i][:2] == "-i": (skip,infile)  = NextArg(i)
            elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
            elif sys.argv[i][:2] == "-h": HelpAndExit()
            elif sys.argv[i][:1] == "-":  Fatal("'%s' unknown argument" % sys.argv[i])
            else:                         Fatal("'%s' unexpected" % sys.argv[i])
        else: skip = 0

    print("Number 1 = %d" % n1)
    print("Number 2 = %d" % n2)
    print("Number 3 = %d" % n3)
    print("Debug    = %d" % debug)
    print("verbose  = %d" % verbose)
    print("infile   = %s" % infile)
    print("outfile  = %s" % outfile) 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top