Zanurkuj w Pythonie/Obsługa argumentów linii poleceń
Obsługa argumentów linii poleceń
[edytuj]Python całkowicie wspomaga tworzenie programów, które mogą zostać uruchomione z linii poleceń, łącznie z argumentami linii poleceń, czy zarówno z krótkim lub długim stylem flag, które określają opcje. Nie ma to nic wspólnego z XML-em, ale omawiany skrypt wykorzystuje w dobry sposób linię poleceń, dlatego też nadeszła odpowiednia pora, aby o nich wspomnieć.
Ciężko mówić o linii poleceń bez wiedzy, w jaki sposób argumenty linii poleceń są ujawniane do programu, dlatego też napiszmy prosty program, aby to zobaczyć.
sys.argv
#argecho.py
import sys
for arg in sys.argv: #(1)
print arg
- Każdy argument linii poleceń przekazany do programu, zostanie umieszczony w
sys.argv
, który jest właściwie listą. W tym miejscu wypisujemy każdy argument w oddzielnej linii.
sys.argv
[you@localhost py]$ python argecho.py #(1) argecho.py [you@localhost py]$ python argecho.py abc def #(2) argecho.py abc def [you@localhost py]$ python argecho.py −−help #(3) argecho.py −−help [you@localhost py]$ python argecho.py −m kant.xml #(4) argecho.py −m kant.xml
- Najpierw musimy sobie uświadomić, że
sys.argv
przechowuje nazwę uruchomionego skryptu. Wiedzę tę wykorzystamy później, w rozdziale "Programowanie funkcyjne". Na razie nie zamartwiaj się tym. - Argumenty linii poleceń są oddzielane przez spacje i każdy z nich ukazuje się w liście
sys.argv
jako oddzielny argument. - Flagi linii poleceń np. −−help, także pokażą się jako osobne elementy w
sys.argv
. - Żeby było ciekawiej, niektóre z flag linii poleceń same przyjmują, wymagają argumentów. Na przykład, tutaj mamy jedną flagę (−m), która na dodatek także przyjmuje argument (w przykładzie kant.xml). Zarówno flaga sama w sobie, a także argument flagi są kolejnymi elementami w liście
sys.argv
. Python w żaden sposób nie będzie próbował ich powiązać; otrzymamy samą listę.
Jak możemy zobaczyć, z pewnością mamy wszystkie informacje przekazane do linii poleceń, ale nie wyglądają na tak proste, aby z nich faktycznie skorzystać. Dla nieskomplikowanych programów, które przyjmują tylko jeden argument bez żadnych flag, możemy po prostu wykorzystać sys.argv[1]
, aby się do niego dostać. Nie ma się czego wstydzić. Dla bardziej złożonych programów będzie potrzebny moduł getopt
.
getopt
def main(argv):
grammar = "kant.xml" #(1)
try:
opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="]) #(2)
except getopt.GetoptError: #(3)
usage() #(4)
sys.exit(2)
# ...
if __name__ == "__main__":
main(sys.argv[1:])
- Od razu zobacz na sam dół przykładu. Wywołujemy funkcję
main
z argumentemsys.argv[1:]
. Zapamiętaj,sys.argv[0]
jest nazwą skryptu, który uruchomiliśmy; nie martwimy się o to, w jaki sposób jest przetwarzana linia poleceń, więc odcinamy i przekazujemy resztę listy. - To najciekawsze miejsce w tym przykładzie. Funkcja
getopt
moduługetopt
przyjmuje trzy parametry: listę argumentów (którą otrzymaliśmy zsys.argv[1:]
), napis zawierający wszystkie możliwe jedno-znakowe flagi, które program akceptuje, a także listę dłuższych flag, które są odpowiednikami krótszych, jedno-znakowych wersji. Na pierwszy rzut oka wydaje się to trochę zamotane, ale w dalszej części szerzej to omówimy. - Jeśli coś nie poszło pomyślnie podczas parsowania flag linii poleceń,
getopt
rzuca wyjątek, który następnie przechwytujemy. Informujemy funkcjęgetopt
o wszystkich flagach, które rozumie nasz program, zatem ostatecznie oznacza, że użytkownik przekazał niektóre niezrozumiałe przez nas flagi. - Jest to praktycznie standard wykorzystywany w świecie Uniksa, kiedy do skryptu zostaną przekazane niezrozumiałe flagi, wypisujemy streszczoną pomoc dotyczącą użycia programu i wdzięcznie zakańczamy go. Dodajmy, że nie przedstawiliśmy tutaj funkcji
usage
. Jeszcze trzeba będzie ją gdzieś zaimplementować, aby wypisywała streszczenie pomocy; nie dzieje się to automatycznie.
Czym są te wszystkie parametry przekazane do funkcji getopt
? Pierwszy jest po prostu surową listą argumentów i flag przekazanych do linii poleceń (bez pierwszego elementu, czyli nazwy skryptu, który wycięliśmy przed wywołaniem funkcji main
). Drugi parametr jest listą krótkich flag linii poleceń, które akceptuje skrypt.
"hg:d" -h wyświetla streszczoną pomoc -g ... korzysta z określonego pliku gramatyki lub URL-a -d pokazuje informacje debugujące podczas parsowania
Pierwsza i trzecia flaga są zwykłymi, samodzielnymi flagami; możemy je określić lub nie. Flagi te wykonują pewne czynności (wypisują pomoc) lub zmieniają stan (włączają debugowanie). Jakkolwiek, za drugą flagą (-g) musi się znaleźć pewien argument, który będzie nazwą pliku gramatyki, który ma zostać wykorzystany. W rzeczywistości może być nazwą pliku lub adresem strony strony web, ale w tym momencie nie wiemy jeszcze, czym jest (zostanie to sprawdzone później), ale wiemy, że ma być czymś. Poinformowaliśmy getopt
o tym, że ma być coś za tą flagą, poprzez wstawienie w drugim parametrze dwukropka po literze g.
Żeby to bardziej skomplikować, skrypt akceptuje zarówno krótkie flagi (np. -h, jak i długie flagi (jak --help), a my chcemy, żeby służyły one do tego samego. I po to jest trzeci parametr w getopt
. Określa on listę długich flag, które odpowiadają krótkim flagom zdefiniowanym w drugim parametrze.
["help", "grammar="] --help wyświetla streszczoną pomoc --grammar ... korzysta z określonego pliku gramatyki lub URL-a
Zwróćmy uwagę na trzy sprawy:
- Wszystkie długie flagi w linii poleceń są poprzedzone dwoma myślnikami, ale podczas wywoływania
getopt
nie dołączamy tych myślników. - Po fladze --grammar musi zawsze wystąpić dodatkowy argument, identycznie jak z flagą -g. Informujemy o tym poprzez znak równości w
"grammar="
. - Lista długich flag jest krótsza niż lista krótkich flag, ponieważ flaga -d nie ma swojego dłuższego odpowiednika. Jedynie -d będzie włączał debugowanie. Jednak porządek krótkich i długich flag musi być ten sam, dlatego też najpierw musimy określić wszystkie krótkie flagi odpowiadające dłuższym flagom, a następnie pozostałą część krótszych flag, które nie mają swojego dłuższego odpowiednika.
W nowych wersjach Pythona 2.7 i 3.2 dobrym pomysłem może być użycie argparse . O zaletach możesz przeczytać na stronie http://code.google.com/p/argparse/ . Moduł ten pozwala na bardziej kompleksową obsługę argumentów niż getopt.
|
Jeszcze się nie pogubiłeś? To spójrz na właściwy kod i zobacz, czy nie staje się dla ciebie zrozumiały.
def main(argv): #(1)
grammar = "kant.xml"
try:
opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts: #(2)
if opt in ("-h", "--help"): #(3)
usage()
sys.exit()
elif opt == '-d': #(4)
global _debug
_debug = 1
elif opt in ("-g", "--grammar"): #(5)
grammar = arg
source = "".join(args) #(6)
k = KantGenerator(grammar, source)
print k.output()
- Zmienna
grammar
będzie przechowywać ścieżkę do pliku gramatyki, z którego będziemy korzystać. W tym miejscu inicjalizujemy ją tak, aby w przypadku, gdy nie zostanie określona w linii poleceń (za pomocą flagi -g lub --grammar) miała jakąś domyślną wartość. - Zmienną
opts
, którą otrzymujemy z wartość zwróconej przezgetopt
, przechowuje listę krotek: flagę i argument. Jeśli flaga nie przyjmuje argumentu, to argument będzie miał wartośćNone
. Ułatwia to wykonywanie pętli na flagach. getopt
kontroluje, czy flagi linii poleceń są akceptowalne, ale nie wykonuje żadnej konwersji między długimi, a krótkimi flagami. Jeśli określimy flagę -h,opt
będzie zawierać"-h"
, natomiast jeśli określimy flagę --help,opt
będzie zawierać"--help"
. Zatem musimy kontrolować obydwa warianty.- Pamiętamy, że fladze -d nie odpowiada żadna dłuższa wersja, dlatego też kontrolujemy tylko tę krótką flagę. Jeśli zostanie ona odnaleziona, ustawiamy globalną zmienną, do której później będziemy się odwoływać, aby wypisywać informacje debugujące. (Flaga ta była wykorzystywana podczas projektowania skryptu. Nie myślisz chyba, że wszystkie przedstawione przykłady działały od razu?)
- Jeśli znajdziemy plik gramatyki spotykając flagę -g lub −−grammar, zapisujemy argument, który następuje po tej fladze (przechowywany w zmiennej
arg
), do zmiennejgrammar
, nadpisując przy tym domyślną wartość, zainicjalizowaną na początku funkcjimain
. - Ok. Wykonaliśmy pętlę przez wszystkie flagi i przetworzyliśmy je. Oznacza to, że pozostała część musi być argumentami linii poleceń, a zostały one zwrócone przez funkcje
getopt
do zmiennejargs
. W tym przypadku traktujemy je jako materiał źródłowy dla parsera. Jeśli nie zostały określone żadne argumenty linii poleceń,args
będzie pustą listą, więcsource
w wyniku tego będzie pustym napisem.