Kommandoradsargument i Python

Översikt

Eftersom Python är ett mycket populärt programmeringsspråk, samt har stöd för de flesta operativsystem och många bibliotek som gör bearbetning av kommandoradsargument lätt – har det blivit allmänt använt för att skapa kommandoradsverktyg för många ändamål. Dessa verktyg kan sträcka sig från enkla CLI-appar till de som är mer komplexa, som AWS' awscli verktyg.

Komplexa verktyg som detta styrs vanligtvis av användaren via kommandoradsargument, som låter användaren använda specifika kommandon, ställa in alternativ och mer. Till exempel kan dessa alternativ berätta för verktyget att mata ut ytterligare information, läsa data från en angiven källa eller skicka utdata till en viss plats.

I allmänhet skickas argument till CLI-verktyg på olika sätt, beroende på ditt operativsystem:

  • Unix-liknande: - följt av ett brev, som -h, eller -- följt av ett ord, som --help
  • Windows: / följt av antingen en bokstav eller ett ord, som /help

Dessa olika tillvägagångssätt finns på grund av historiska skäl. Många program på Unix-liknande system stöder både enkel- och dubbelstreck. Den enda bindestrecksbeteckningen används mest med enbokstavsalternativ, medan dubbla bindestreck ger en mer läsbar alternativlista, vilket är särskilt användbart för komplexa alternativ som behöver vara mer explicita.

Anmärkningar: I den här artikeln kommer vi enbart att fokusera på det Unix-liknande formatet - och --.

Tänk på att både namnet och innebörden av ett argument är specifika för ett program – det finns ingen generell definition, förutom några vanliga konventioner som --help för ytterligare information om användningen av verktyget. Som utvecklare av ett Python-skript kommer du att bestämma vilka argument som ska tillhandahållas till den som ringer och vad de gör. Detta kräver en ordentlig utvärdering.

När din lista över tillgängliga argument växer kommer din kod att bli mer komplex när du försöker analysera dem korrekt. Lyckligtvis finns det i Python ett antal bibliotek tillgängliga för att hjälpa dig med detta. Vi kommer att täcka några av de vanligaste lösningarna, som sträcker sig från "gör-det-själv" med sys.argv, till "gjort-för-dig"-metoden med argparse.

Hantera kommandoradsargument med Python

Python 3+ och ekosystemet runt stödjer ett antal olika sätt att hantera kommandoradsargument. Det finns många bibliotek som fascinerar analys av kommandoradsargument.

Det inbyggda sättet är att använda sys modul. När det gäller namn och dess användning relaterar den direkt till C-biblioteket (libc).

Det andra sättet är getopt modul, som hanterar både korta och långa alternativ, inklusive utvärdering av parametervärdena.

Smakämnen argparse-modul, som härrör från optparse modul (tillgänglig upp till Python 2.7).

Smakämnen docopt modul, vilket är tillgänglig på GitHub, tillåter också samma funktionalitet.

Nyligen, den absl biblioteket har också fått fart, som ett sätt att ersätta optparse och getopt().

Vart och ett av dessa sätt har sina för- och nackdelar, så det är värt att utvärdera vart och ett för att se vilket som passar dina behov bäst.

Sys-modulen

Detta är en grundläggande modul som har levererats med Python från början. Det tar ett mycket liknande tillvägagångssätt som C-biblioteket använder argc/argv för att komma åt argumenten. De sys-modul implementerar kommandoradsargumenten i en enkel liststruktur med namnet sys.argv.

Varje listelement representerar ett enda argument. Det första objektet i listan, sys.argv[0], är namnet på Python-skriptet. Resten av listelementen, sys.argv[1] till sys.argv[n], är kommandoradsargumenten 2 till n.

Som avgränsare mellan argumenten används ett mellanslag. Argumentvärden som innehåller ett mellanslag måste omges av citattecken för att kunna tolkas korrekt av sys.

Motsvarande av argc är bara antalet element i listan. För att få detta värde, använd Python len() operatör. Vi kommer att visa detta i ett kodexempel senare.

Skriver ut det första CLI-argumentet

I det här första exemplet kommer vårt skript att avgöra hur det kallades. Denna information hålls i det första kommandoradsargumentet, indexerat med 0. Koden nedan visar hur du får namnet på ditt Python-skript:

import sys

print("The script has the name %s" % (sys.argv[0])

Spara denna kod i en fil med namnet arguments-program-name.py, och kalla det sedan som visas nedan. Utdata är som följer och innehåller filnamnet, inklusive dess fullständiga sökväg:

$ python arguments-program-name.py
The script has the name arguments-program-name.py
$ python /home/user/arguments-program-name.py
The script has the name /home/user/arguments-program-name.py

Som du kan se från det andra anropet ovan får vi inte bara namnet på Python-filen, utan också den fullständiga sökvägen som används för att anropa den.

Räkna antalet argument

I det här andra exemplet räknar vi helt enkelt antalet kommandoradsargument med det inbyggda len() metod. sys.argv är listan som vi måste granska. I koden nedan får vi antalet argument och subtraherar sedan 1 eftersom ett av dessa argument (dvs det första) alltid är inställt som namnet på filen, vilket inte alltid är användbart för oss. Således är det faktiska antalet argument som skickas av användaren len(sys.argv) - 1:

import sys


arguments = len(sys.argv) - 1
print ("The script is called with %i arguments" % (arguments))

Spara och namnge denna fil arguments-count.py. Några exempel på att anropa detta skript visas nedan. Detta inkluderar tre olika scenarier:

  • Ett anrop utan ytterligare kommandoradsargument
  • Ett samtal med två argument
  • Ett anrop med två argument, där det andra är en citattecken som innehåller ett mellanslag
$ python arguments-count.py
The script is called with 0 arguments
$ python arguments-count.py --help me
The script is called with 2 arguments
$ python arguments-count.py --option "long string"
The script is called with 2 arguments
Itererande genom argument

Vårt tredje exempel matar ut varje enskilt argument som skickas till Python-skriptet, förutom själva programnamnet. Därför går vi igenom kommandoradsargumenten som börjar med 2:a listelement. Kom ihåg att detta är index 1 eftersom listor är 0-baserade i Python:

import sys


arguments = len(sys.argv) - 1


position = 1
while (arguments >= position):
    print ("Parameter %i: %s" % (position, sys.argv[position]))
    position = position + 1

Nedan anropar vi vår kod, som sparades i filen arguments-output.py. Som gjort med vårt tidigare exempel illustrerar utdata tre olika anrop:

  • Ett samtal utan några argument
  • Ett samtal med två argument
  • Ett anrop med två argument, där det andra argumentet är en citattäng som innehåller ett mellanslag
$ python arguments-output.py
$ python arguments-output.py --help me
Parameter 1: --help
Parameter 2: me
$ python arguments-output.py --option "long string"
Parameter 1: --option
Parameter 2: long string

Kom ihåg att poängen med att visa exemplet med citerade strängar är att parametrar vanligtvis avgränsas av ett mellanslag, såvida inte de är omgivna av citat.

Abseil Flaggor (absl)

Abseils Flags-bibliotek är tänkt att ta kommandoradsargument till produktion, med distribuerade kommandoradsargument. När en modul använder kommandoradsflaggor och importeras till en annan modul – den andra modulen importerar även flaggorna, och kan bearbeta dem genom att vidarebefordra dem till den importerade modulen.

Detta gör komplexa kommandoradsargument som delas mellan moduler enklare och mindre omfattande.

Dessutom låter biblioteket dig definiera standardvärden, beskrivningar av och datatyp för argumenten, så ytterligare kontroller och konverteringar är inte nödvändiga.

from absl import flags
import sys


flags.DEFINE_string('name', 'User', 'The name of the user.')


FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"Hello {FLAGS.name}!")

De datatyper som stöds är:

  • DEFINE_integer()
  • DEFINE_string()
  • DEFINE_bool()
  • DEFINE_enum()
  • DEFINE_list()
  • DEFINE_float()

Förutom DEFINE_multi_integer(), DEFINE_multi_string() och DEFINE_multi_enum() för inmatning av flera argument. Dessutom löpning --help, --helpfull, etc. skriva ut befintliga flaggor och deras beskrivningar, i olika format.

Kolla in vår praktiska, praktiska guide för att lära dig Git, med bästa praxis, branschaccepterade standarder och medföljande fuskblad. Sluta googla Git-kommandon och faktiskt lära Det!

Biblioteket låter dig också definiera valideringar – både när det gäller intervall, till exempel heltalsbaserade värden som har en upper_bound or lower_bound det är acceptabelt och kör godtyckliga metoder för att söka efter värden:

def validate_name(value):
    return len(value) > 15

flags.register_validator('name',
                         validate_name,
                         message='Name is over 15 characters long.',
                         flag_values=FLAGS)

Samla dessa till ett konkret exempel:

from absl import flags
import sys

flags.DEFINE_string('name', 'User', 'The name of the user.')
flags.DEFINE_integer('tasks', 0, 'The number of tasks a user has.', lower_bound=0)

FLAGS = flags.FLAGS
FLAGS(sys.argv)

print(f"{FLAGS.name} has {FLAGS.tasks} tasks to work on.")
$ python flags.py --name=John --tasks=5
John has 5 tasks to work on.
$ python flags.py --name=John --tasks=-1

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_flag.py", line 180, in _parse
    return self.parser.parse(argument)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/absl/flags/_argument_parser.py", line 168, in parse
    raise ValueError('%s is not %s' % (val, self.syntactic_help))
ValueError: -1 is not a non-negative integer
...

Argparse-modulen

Smakämnen argparse-modul har varit tillgänglig sedan Python 3.2, och en förbättring av optparse modul som finns upp till Python 2.7. Python-dokumentationen innehåller en API-beskrivning och en handledning som täcker alla metoder i detalj.

Modulen erbjuder ett kommandoradsgränssnitt med en standardiserad utgång, medan de två tidigare lösningarna lämnar mycket av arbetet i dina händer. argparse tillåter verifiering av fasta och valfria argument, med namnkontroll som antingen kort eller lång stil. Som ett valfritt standardargument inkluderar det -h, tillsammans med dess långa version --help. Detta argument åtföljs av ett standardhjälpmeddelande som beskriver de accepterade argumenten.

Koden nedan visar parserinitieringen och utgången nedan som visar det grundläggande anropet, följt av hjälpmeddelandet. I motsats till Python-anropen vi använde i de tidigare exemplen, kom ihåg att använda Python 3 med dessa exempel:


import argparse


parser = argparse.ArgumentParser()
parser.parse_args()
$ python3 arguments-argparse-basic.py 
$ python3 arguments-argparse-basic.py -h
usage: arguments-argparse-basic.py [-h]

optional arguments:
  -h, --help  show this help message and exit
$ python3 arguments-argparse-basic.py --verbose
usage: arguments-argparse-basic.py [-h]
arguments-argparse-basic.py: error: unrecognized arguments: --verbose

I nästa steg kommer vi att lägga till en anpassad beskrivning i hjälpmeddelandet för våra användare. Att initiera tolken på detta sätt tillåter ytterligare text. Koden nedan lagrar beskrivningen i text variabel, som uttryckligen ges till argparse klass som description parameter. Genom att anropa den här koden nedan kan du se hur utgången ser ut:


import argparse


text = 'This is a test program. It demonstrates how to use the argparse module with a program description.'


parser = argparse.ArgumentParser(description=text)
parser.parse_args()
$ python3 arguments-argparse-description.py --help
usage: arguments-argparse-description.py [-h]

This is a test program. It demonstrates how to use the argparse module with a
program description.

optional arguments:
  -h, --help  show this help message and exit

Som det sista steget kommer vi att lägga till ett valfritt argument med namnet -V, som har ett motsvarande långt stilargument som heter --version. För att göra det använder vi metoden add_argument() som vi anropar med tre parametrar (visas för --version, endast):

  • Namnet på parametern: --version
  • Hjälptexten för parametern: help="show program version"
  • Åtgärd (utan extra värde): action="store_true"

Källkoden för det visas nedan. Läsa argumenten i variabeln kallas args sker via parse_args() metod från parser objekt. Observera att du skickar in både den korta och den långa versionen i ett samtal. Slutligen kontrollerar du om attributen args.V or args.version ställs in och matar ut versionsmeddelandet:


import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-V", "--version", help="show program version", action="store_true")


args = parser.parse_args()


if args.version:
    print("This is myprogram version 0.1")
$ python3 arguments-argparse-optional.py -V
This is myprogram version 0.1
$ python3 arguments-argparse-optional.py --version
This is myprogram version 0.1

Smakämnen --version argument kräver inte att ett värde anges på kommandoraden. Det är därför vi ställer in handlingsargumentet till "store_true". I andra fall kan du behöva ett extra tilldelat värde, till exempel om du anger en viss volym, höjd eller bredd. Detta visas i nästa exempel. Som ett standardfall, observera att alla argument tolkas som strängar:


import argparse


parser = argparse.ArgumentParser()


parser.add_argument("--width", "-w", help="set output width")


args = parser.parse_args()


if args.width:
    print("Set output width to %s" % args.width)

Här visar vi vad som händer när man skickar in olika argumentvärden. Detta inkluderar både den korta och den långa versionen, såväl som hjälpmeddelandet:

$ python3 arguments-argparse-optional2.py -w 10
Set output width to 10
$ python3 arguments-argparse-optional2.py --width 10
Set output width to 10
$ python3 arguments-argparse-optional2.py -h
usage: arguments-argparse-optional2.py [-h] [--width WIDTH]

optional arguments:
  -h, --help            show this help message and exit
  --width WIDTH, -w WIDTH
                        set output width

Getopt-modulen

Som du kanske har märkt tidigare, sys modulen delar upp kommandoradssträngen endast i enstaka fasetter. Pytonen getopt-modul går lite längre och utökar separationen av inmatningssträngen genom parametervalidering. Baserat på getopt C-funktion, den tillåter både korta och långa alternativ, inklusive en värdetilldelning.

I praktiken kräver det sys modul för att bearbeta indata korrekt. För att göra det, både sys modul och getopt modulen måste laddas i förväg. Därefter tar vi bort det första listelementet från listan med inmatningsparametrar (se koden nedan) och lagrar den återstående listan med kommandoradsargument i variabeln som heter argument_list:


import getopt, sys


full_cmd_arguments = sys.argv


argument_list = full_cmd_arguments[1:]

print argument_list

Argumenten i argument_list kan nu tolkas med hjälp av getopts() metod. Men innan vi gör det måste vi berätta getopts() om vilka parametrar som är giltiga. De definieras så här:

short_options = "ho:v"
long_options = ["help", "output=", "verbose"]

Det betyder att dessa argument är de som vi anser vara giltiga, tillsammans med lite extra info:

------------------------------------------
long argument   short argument  with value
------------------------------------------
--help           -h              no
--output         -o              yes
--verbose        -v              no
------------------------------------------

Du kanske har märkt att o kort alternativ följdes av ett kolon, :. Detta berättar getopt att detta alternativ ska tilldelas ett värde.

Detta tillåter oss nu att bearbeta en lista med argument. De getopt() Metoden kräver att tre parametrar konfigureras – listan över faktiska argument från argv, såväl som de giltiga korta och långa alternativen (visas i föregående kodavsnitt).

Själva metodanropet hålls i ett try-catch-statement för att täcka fel under utvärderingen. Ett undantag tas upp om ett argument upptäcks som inte ingår i listan enligt tidigare definition. Python-skriptet kommer att skriva ut felmeddelandet på skärmen och avsluta med felkod 2:

try:
    arguments, values = getopt.getopt(argument_list, short_options, long_options)
except getopt.error as err:
    
    print (str(err))
    sys.exit(2)

Slutligen lagras argumenten med motsvarande värden i de två namngivna variablerna arguments och values. Nu kan du enkelt utvärdera dessa variabler i din kod. Vi kan använda en for-loop för att iterera genom listan med igenkända argument, en post efter nästa.


for current_argument, current_value in arguments:
    if current_argument in ("-v", "--verbose"):
        print ("Enabling verbose mode")
    elif current_argument in ("-h", "--help"):
        print ("Displaying help")
    elif current_argument in ("-o", "--output"):
        print (("Enabling special output mode (%s)") % (current_value))

Nedan kan du se resultatet från exekvering av denna kod. Vi visar hur programmet reagerar med både giltiga och ogiltiga programargument:

$ python arguments-getopt.py -h
Displaying help
$ python arguments-getopt.py --help
Displaying help
$ python arguments-getopt.py --output=green --help -v
Enabling special output mode (green)
Displaying help
Enabling verbose mode
$ python arguments-getopt.py -verbose
option -e not recognized

Det sista samtalet till vårt program kan verka lite förvirrande till en början. För att förstå det måste du veta att stenografialternativen (ibland även kallade flaggor) kan användas tillsammans med ett enda streck. Detta gör att ditt verktyg lättare accepterar många alternativ. Till exempel att ringa python arguments-getopt.py -vh är detsamma som att ringa python arguments-getopt.py -v -h. Så i det sista samtalet ovan getopt modulen trodde att användaren försökte passera -e som ett alternativ, vilket är ogiltigt.

Slutsats

I den här artikeln visade vi många olika metoder för att hämta kommandoradsargument i Python, inklusive att använda sys, getoptoch argparse. Dessa moduler varierar i funktionalitet, vissa ger mycket mer än andra. sys är fullt flexibel, medan båda getoptoch argparse kräver lite struktur. Däremot täcker de det mesta av det komplexa arbetet som sys lämnar upp till dig. Efter att ha arbetat igenom exemplen som tillhandahålls bör du kunna avgöra vilken modul som passar ditt projekt bäst.

I den här artikeln pratade vi inte om andra lösningar som docopts modul, vi nämnde det precis. Denna modul följer ett helt annat tillvägagångssätt och kommer att förklaras i detalj i en av de följande artiklarna.

Referensprojekt

Tidsstämpel:

Mer från Stackabuse