Comment lire / traiter les arguments de la ligne de commande?

625

Je suis à l'origine un programmeur C. J'ai vu de nombreuses astuces et "hacks" pour lire de nombreux arguments différents.

De quelles manières les programmeurs Python peuvent-ils procéder?

en relation

martineau
la source
5
Utilisez docopt (voir la réponse de @ ralbatross à stackoverflow.com/a/14790373/116891 ). J'ai essayé de toutes les autres façons et, vraiment, docopt est le seul que j'utiliserai à l'avenir.
Pat
2
Je ne pense pas qu'il existe une seule meilleure façon. argparse est standard et fonctionnel. docopt est très élégant mais pas dans la bibliothèque standard. Pour une utilisation très simple et légère, vous pouvez faire en sorte que les valeurs par défaut des fonctions gèrent les valeurs par défaut des arguments de ligne de commande .
Simon Hibbs

Réponses:

457

La solution canonique dans la bibliothèque standard est argparse( docs ):

Voici un exemple:

from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
                    help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
                    action="store_false", dest="verbose", default=True,
                    help="don't print status messages to stdout")

args = parser.parse_args()

argparse soutient (entre autres):

  • Plusieurs options dans n'importe quel ordre.
  • Options courtes et longues.
  • Les valeurs par défaut.
  • Génération d'un message d'aide à l'utilisation.
Ayman Hourieh
la source
27
Oui, ce sont les meilleurs. Comme ils font partie de la bibliothèque standard, vous pouvez être sûr qu'ils seront disponibles et faciles à utiliser. optparse en particulier est puissant et facile.
Barry Wark
4
optparse est l'un des meilleurs; getopt est vieux et doit vraiment être considéré comme obsolète.
jemfinch
12
à ce stade (12/2011), argparse est désormais considéré comme une meilleure option que optparse, n'est-ce pas?
oob
54
La documentation Python suggère d'utiliser argparse au lieu de optparse.
earthmeLon
7
Puisque optparseest obsolète, le demandeur de la question n'est plus membre sur le débordement de pile, et c'est la réponse acceptée sur une question très visible - veuillez envisager de réécrire complètement votre exemple de code pour utiliser stdlib à la argparseplace.
wim
548
import sys

print("\n".join(sys.argv))

sys.argv est une liste qui contient tous les arguments passés au script sur la ligne de commande.

Fondamentalement,

import sys
print(sys.argv[1:])
John Slavick
la source
83
Pour des choses vraiment simples, c'est la voie à suivre, bien que vous ne souhaitiez probablement utiliser que sys.argv[1:](évite le nom du script).
Xiong Chiamiov
128

Faire le tour de l'évangélisation pour argparse, ce qui est mieux pour ces raisons .. essentiellement:

(copié à partir du lien)

  • Le module argparse peut gérer les arguments positionnels et optionnels, tandis que optparse ne peut gérer que les arguments optionnels

  • argparse n'est pas dogmatique sur ce à quoi devrait ressembler votre interface de ligne de commande - les options comme -file ou / file sont prises en charge, tout comme les options requises. Optparse refuse de prendre en charge ces fonctionnalités, préférant la pureté à la praticité

  • argparse produit des messages d'utilisation plus informatifs, y compris l'utilisation de la ligne de commande déterminée à partir de vos arguments, et des messages d'aide pour les arguments positionnels et facultatifs. Le module optparse vous oblige à écrire votre propre chaîne d'utilisation et n'a aucun moyen d'afficher l'aide pour les arguments positionnels.

  • argparse prend en charge les actions qui consomment un nombre variable d'arguments de ligne de commande, tandis qu'optparse requiert que le nombre exact d'arguments (par exemple 1, 2 ou 3) soit connu à l'avance.

  • argparse prend en charge les analyseurs qui expédient vers les sous-commandes, tandis qu'optparse nécessite de définir allow_interspersed_argset d'effectuer la répartition de l'analyseur manuellement

Et mon préféré:

  • argparse permet de spécifier les paramètres de type et d'action add_argument() avec de simples callables, tandis qu'optparse nécessite le piratage d'attributs de classe comme STORE_ACTIONSou CHECK_METHODSpour obtenir une vérification correcte des arguments
Silfheed
la source
27
Cela fait maintenant partie de Python standard à partir de 2.7 et 3.2 :)
jpswain
Que sont les "arguments facultatifs"? Vous dites qu'ils sont en optparse. Je pensais que c'étaient des arguments qui pouvaient ou non être fournis, mais vous avez dit qu'ils étaient en optparse tout en disant que "optparse exige que le nombre exact d'arguments soit connu à l'avance". Donc, soit votre définition de «l'argument facultatif» diffère de ce que je pensais, soit votre réponse n'est pas cohérente avec elle-même.
ArtOfWarfare
1
Juste un reproche: la documentation argparse est aussi incroyablement, incroyablement compliquée. Vous ne pouvez pas obtenir une réponse simple pour "comment puis-je faire prendre un argument de ligne de commande dans une seule valeur, et comment puis-je accéder à cette valeur." </gripe>
osman
2
@osman Ce doux tutoriel sur argparse pourrait aider ...
lifebalance
2
@ArtOfWarfare "arguments facultatifs" dans ce contexte signifie vraisemblablement des arguments spécifiés avec des arguments de type option tels que -fou --foo, tandis que "le nombre exact d'arguments connus à l'avance" signifie probablement des arguments positionnels donnés sans aucun indicateur d'option précédent.
mtraceur
67

Il y a aussi le argparsemodule stdlib (une "amélioration" sur le optparsemodule stdlib ). Exemple d' introduction à argparse :

# script.py
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'integers', metavar='int', type=int, choices=range(10),
         nargs='+', help='an integer in the range 0..9')
    parser.add_argument(
        '--sum', dest='accumulate', action='store_const', const=sum,
        default=max, help='sum the integers (default: find the max)')

    args = parser.parse_args()
    print(args.accumulate(args.integers))

Usage:

$ script.py 1 2 3 4
4

$ script.py --sum 1 2 3 4
10
jfs
la source
1
c'est juste un copier-coller
blitu12345
3
@ blitu12345 au moment de la publication de ma réponse, il n'y avait aucune autre réponse qui mentionnait argparse de quelque façon que ce soit. Le module lui-même n'était pas dans stdlib¶ Qu'avez-vous contre les exemples de code de la documentation? Pourquoi pensez-vous qu'il est nécessaire de proposer vos propres exemples au lieu d'exemples fournis par l'auteur du module? Et je n'aime pas les réponses liées uniquement aux liens (je ne suis pas seul).
jfs
1
Les personnes qui venaient ici avaient déjà une idée de ce qui se trouve dans la documentation et ne seront là que pour plus de clarté sur le sujet. C'était mon cas, mais ce que j'ai vraiment trouvé ici est un copier-coller des documents originaux. Paix!
blitu12345
2
"Les gens qui viennent ici avaient déjà une idée de ce qui se trouve dans la documentation" - je doute fortement de cette hypothèse. en quelque sorte.
sjas
49

Une façon de le faire consiste à utiliser sys.argv. Cela affichera le nom du script comme premier argument et tous les autres paramètres que vous lui passerez.

import sys

for arg in sys.argv:
    print arg
JPCosta
la source
49

La bibliothèque docopt est vraiment épurée . Il construit un argument argument à partir de la chaîne d'utilisation de votre application.

Par exemple, dans le fichier Lisezmoi de docopt:

"""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)
ralbatross
la source
4
C'est rapidement devenu mon chemin préféré. C'est une analyse de chaîne, donc c'est un peu fragile, mais c'est fragile au même endroit et vous pouvez prévisualiser votre logique sur try.docopt.org . Les arguments facultatifs et mutuellement exclusifs sont élaborés de manière très élégante.
gvoysey
4
Je suis désespéré de voir le reste du code pour naval_fate.py
John Lawrence Aspden
48

Si vous avez besoin de quelque chose de rapide et pas très flexible

main.py:

import sys

first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)

Ensuite, exécutez python main.py James Smith

pour produire la sortie suivante:

Bonjour James Smith

Kent Munthe Caspersen
la source
Une utilisation plus réaliste serait ce python main.py "James Smith"qui met James Smithen sys.argv[1]et produit un IndexErrorlorsque vous essayez d'utiliser l'inexistant sys.argv[2]. Le comportement des citations dépendra quelque peu de la plateforme et du shell à partir desquels vous exécutez Python.
tripleee
10
Je ne suis pas d'accord pour dire que mon utilisation est moins réaliste. Imaginez que votre programme a besoin de connaître le prénom et le nom exact d'une personne pour exécuter le script dans une entreprise où les gens peuvent avoir plusieurs prénom et nom? Si James Smith a Joseph comme prénom ou nom de famille supplémentaire, comment distinguer si Joseph est un prénom ou nom de famille supplémentaire si vous le faites seulement python main.py "James Joseph Smith"? Si vous êtes concerné par l'index hors limites, vous pouvez ajouter une vérification du nombre d'arguments fournis. Moins réaliste ou pas, mon exemple montre comment gérer plusieurs arguments.
Kent Munthe Caspersen
1
Toutes les autres réponses concernent le tracé d'une mission d'atterrissage lunaire. J'utilise simplement gmail-trash-msg.py MessageID. Cette réponse est simple pour tester le MessageIDparamètre a été transmis sys.argv[1].
WinEunuuchs2Unix
26
#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]
whi
la source
19

J'utilise optparse moi-même, mais j'aime vraiment la direction que prend Simon Willison avec sa bibliothèque optfunc récemment introduite . Il fonctionne par:

"introspecter une définition de fonction (y compris ses arguments et leurs valeurs par défaut) et l'utiliser pour construire un analyseur d'arguments en ligne de commande."

Ainsi, par exemple, cette définition de fonction:

def geocode(s, api_key='', geocoder='google', list_geocoders=False):

est transformé en ce texte d'aide optparse:

    Options:
      -h, --help            show this help message and exit
      -l, --list-geocoders
      -a API_KEY, --api-key=API_KEY
      -g GEOCODER, --geocoder=GEOCODER
Van Gale
la source
8

J'aime getopt de stdlib, par exemple:

try:
    opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err: 
    usage(err)

for opt, arg in opts:
    if opt in ('-h', '--help'): 
        usage()

if len(args) != 1:
    usage("specify thing...")

Dernièrement, j'ai enveloppé quelque chose de similaire à cela pour rendre les choses moins verbeuses (par exemple, rendre "-h" implicite).

Peter Ericson
la source
8

De Pocoo click est plus intuitif, nécessite moins de passe-partout et est au moins aussi puissant que l'argparse.

La seule faiblesse que j'ai rencontrée jusqu'à présent est que vous ne pouvez pas faire beaucoup de personnalisation pour aider les pages, mais ce n'est généralement pas une exigence et docopt semble être le choix clair quand il l'est.

Ryne Everett
la source
7

Comme vous pouvez le voir optparse "Le module optparse est obsolète et ne sera pas développé davantage; le développement se poursuivra avec le module argparse ."

tverrbjelke
la source
5
import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                   help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                   const=sum, default=max,
                   help='sum the integers (default: find the max)')

args = parser.parse_args()
print(args.accumulate(args.integers))

Assuming the Python code above is saved into a file called prog.py
$ python prog.py -h

Ref-link: https://docs.python.org/3.3/library/argparse.html
JON
la source
4

Vous pourriez être intéressé par un petit module Python que j'ai écrit pour rendre la gestion des arguments de ligne de commande encore plus facile (open source et gratuit à utiliser) - Commando

Mufasa
la source
1
Il existe déjà un autre module d'analyse de ligne de commande nommé Commando: github.com/lakshmivyas/commando . Il enveloppe argparse en utilisant des décorateurs.
Roberto Bonvallet
1
réinvention du python et de la roue
Derek
4

Je recommande de regarder docopt comme une alternative simple à ces autres.

docopt est un nouveau projet qui fonctionne en analysant votre message d'utilisation --help plutôt qu'en vous obligeant à tout implémenter vous-même. Il vous suffit de mettre votre message d'utilisation au format POSIX.

David C. Bishop
la source
4

Pourtant, une autre option est argh . Il s'appuie sur argparse et vous permet d'écrire des choses comme:

import argh

# declaring:

def echo(text):
    "Returns given word as is."
    return text

def greet(name, greeting='Hello'):
    "Greets the user with given name. The greeting is customizable."
    return greeting + ', ' + name

# assembling:

parser = argh.ArghParser()
parser.add_commands([echo, greet])

# dispatching:

if __name__ == '__main__':
    parser.dispatch()

Il générera automatiquement de l'aide, etc., et vous pouvez utiliser des décorateurs pour fournir des conseils supplémentaires sur la façon dont l'analyse d'arg doit fonctionner.

ruine circulaire
la source
C'est la meilleure solution. L'utilisation arghest plus facile qu'une autre bibliothèque ou l'utilisation sys.
Juanjo Salvador
Je voulais aimer, arghmais ce n'est pas particulièrement adapté aux scénarios où votre plus grand désir n'est pas d'avoir une commande avec des sous-commandes.
tripleee
1
@tripleee YMMV, mais j'ai trouvé que c'était plus un défaut dans la documentation que dans la bibliothèque elle-même. Il semble parfaitement possible de def frobnicate_spleches(...)définir une fonction qui fait ce que fait votre script, puis de le faire if __name__ == '__main__': argh.dispatch_command(frobnicate_spleches)à la fin du fichier.
circulaire-ruine
0

Ma solution est entrypoint2 . Exemple:

from entrypoint2 import entrypoint
@entrypoint
def add(file, quiet=True): 
    ''' This function writes report.

    :param file: write report to FILE
    :param quiet: don't print status messages to stdout
    '''
    print file,quiet

Texte d'aide:

usage: report.py [-h] [-q] [--debug] file

This function writes report.

positional arguments:
  file         write report to FILE

optional arguments:
  -h, --help   show this help message and exit
  -q, --quiet  don't print status messages to stdout
  --debug      set logging level to DEBUG
ponty
la source