Cum să analizați argumentele liniei de comandă în Python

Te întrebi cum să execuți scripturi Python folosind argumente în linia de comandă? Descoperă cum să interpretezi aceste argumente cu ajutorul modulelor sys, getopt și argparse din Python.

În mod obișnuit, când dorești să primești date de la utilizator în Python, apelezi la funcția `input()`. Totuși, în anumite scenarii, poate fi necesar să transmiți direct argumente în momentul executării scriptului, prin intermediul liniei de comandă.

Acest ghid îți va arăta cum să rulezi un script Python cu diverse opțiuni și argumente, direct din linia de comandă. Vei învăța să utilizezi modulele predefinite ale Python pentru a analiza aceste opțiuni și argumente.

Să începem!

Explorând `sys.argv` în Python

Dacă ai experiență cu limbajul C, știi că un mod simplu de a trimite argumente unui program este prin intermediul liniei de comandă. În C, poți organiza funcția principală astfel:

#include<stdio.h>

int main(int argc, char **argv){
    //argc: numărul de argumente
    //argv: vectorul de argumente
    
    //prelucrează argumentele aici

    return 0;
}

Aici, `argc` reține numărul de argumente transmise, iar `argv` este un vector care le stochează.

Executarea scripturilor Python cu argumente din linia de comandă

În Python, pentru a rula un script, folosești comanda `python3 filename.py`. Odată cu aceasta, poți adăuga un număr variabil de argumente din linia de comandă:

$ python3 filename.py arg1 arg2 ... argn

Modulul `sys` oferă instrumente pentru a accesa și gestiona aceste argumente. `sys.argv` este o listă care include toate argumentele transmise la rularea scriptului Python.

Un exemplu de rulare a `main.py` cu argumente din linia de comandă:

$ python3 main.py hello world python script

Putem itera prin vectorul de argumente folosind o buclă simplă și funcția `enumerate`:

# main.py

import sys

for idx, arg in enumerate(sys.argv):
    print(f"arg{idx}: {arg}")
# Ieșire
arg0:main.py
arg1:hello
arg2:world
arg3:python
arg4:script

Observăm că primul argument (indexul 0) este numele fișierului Python, iar argumentele ulterioare încep de la indexul 1.

Acesta este un program de bază care primește și prelucrează argumentele liniei de comandă. Însă, apar câteva neclarități:

  • Cum știu utilizatorii ce argumente să introducă?
  • Ce reprezintă exact aceste argumente?

Pentru a clarifica aceste aspecte, poți folosi modulele `getopt` sau `argparse`. În secțiunile următoare, vom explora aceste module.✅

Analiza argumentelor liniei de comandă cu `getopt` în Python

Vom învăța cum să interpretăm argumentele liniei de comandă folosind modulul `getopt` inclus în Python.

După ce importăm modulul `getopt`, putem specifica argumentele de analizat, opțiunile scurte și opțiunile lungi. Este necesar să analizăm argumentele începând cu indexul 1 din `sys.argv`. Astfel, porțiunea de analizat este `sys.argv[1:]`.

Pentru exemplificare, vom avea nevoie de un mesaj și un nume de fișier. Vom folosi `m` și `f` ca opțiuni scurte, iar `message` și `file` ca opțiuni lungi.

Cum ne asigurăm că o anumită opțiune necesită un argument?

  • Pentru opțiunile scurte, adaugă două puncte (:) după numele scurt al opțiunii.
  • Similar, pentru opțiunile lungi, adaugă semnul = după opțiunea lungă. Astfel, putem capta aceste opțiuni și argumentele asociate.

Cu aceste adăugiri, codul din `main.py` va arăta astfel:

# main.py

import sys
from getopt import getopt

opts, args = getopt(sys.argv[1:],'m:f:',['message=','file='])

print(opts)
print(args)

În acest cod, variabila `opts` va reține opțiunile și argumentele ca o listă de tupluri. Orice alt argument pozițional transmis va fi stocat în variabila `args`.

Putem trimite mesajul și numele fișierului la rularea scriptului, folosind fie opțiunile scurte, fie cele lungi.

Iată cum arată executarea `main.py` folosind opțiunile lungi:

$ python3 main.py --message hello --file somefile.txt

Opțiunile și argumentele sunt stocate ca tupluri în variabila `opts`. Deoarece nu am introdus argumente poziționale, `args` va fi o listă goală.

# Ieșire
[('--message', 'hello'), ('--file', 'somefile.txt')]
[]

În mod similar, putem folosi opțiunile scurte, după cum urmează:

$ python3 main.py -m hello -f somefile.txt
# Ieșire
[('-m', 'hello'), ('-f', 'somefile.txt')]
[]

⚠️ Opțiunea scurtă `-m` din acest exemplu nu trebuie confundată cu indicatorul liniei de comandă `-m`, care rulează un modul ca modul principal la executarea scriptului Python.

De exemplu, `python3 -m unittest main.py` rulează `unittest` ca modul principal când execuți `main.py`.

Am menționat că orice alte argumente poziționale sunt stocate în variabila `args`. Iată un exemplu:

$ python3 main.py -m hello -f somefile.txt another_argument

Lista de argumente include argumentul pozițional `another_argument`.

# Ieșire
[('-m', 'hello'), ('-f', 'somefile.txt')]
['another_argument']

Având `opts` ca o listă de tupluri, putem itera prin ea, despachetând fiecare tuplu pentru a extrage argumentele asociate opțiunilor respective.

Ce facem cu numele fișierului și mesajul, odată ce le-am preluat? Vom deschide fișierul în modul scriere și vom adăuga mesajul transformat în majuscule.

# main.py
import sys
from getopt import getopt

opts, args = getopt(sys.argv[1:],'m:f:',['message=','file='])

print(opts)
print(args)

for option, argument in opts:
    if option == '-m':
        message = argument
    if option == '-f':
        file = argument

with open(file,'w') as f:
    f.write(message.upper())

Rulăm `main.py` cu opțiunile scurte și argumentele din linia de comandă.

$ python main.py -m hello -f thisfile.txt
[('-m', 'hello'), ('-f', 'thisfile.txt')]
[]

După executarea `main.py`, vom observa `thisfile.txt` în directorul curent. Acesta conține mesajul „hello” transformat în majuscule („HELLO”).

$ ls
main.py  thisfile.txt
$ cat thisfile.txt
HELLO

Analizarea argumentelor liniei de comandă cu Argparse

Modulul `argparse`, inclus în biblioteca standard Python, oferă funcționalități pentru analiza argumentelor liniei de comandă și construirea interfețelor pentru aceasta.

Pentru analiza argumentelor liniei de comandă, importăm clasa `ArgumentParser` din modulul `argparse`. Aici, am creat o instanță `arg_parser`, un obiect `ArgumentParser`:

from argparse import ArgumentParser

arg_parser = ArgumentParser()

Apoi, dorim să adăugăm două argumente din linia de comandă:

  • `message`: șirul de mesaj și
  • `file`: numele fișierului cu care lucrăm.

Apelăm metoda `add_argument()` pe `arg_parser` pentru a adăuga aceste argumente. În cadrul apelului, poți stabili un șir de ajutor (o descriere a argumentului) cu parametrul `help`.

arg_parser.add_argument('message',help='șir de mesaj')
arg_parser.add_argument('file',help='nume fișier')

Până acum, am instanțiat `arg_parser` și am adăugat argumentele liniei de comandă. Când programul este executat, poți folosi metoda `parse_args()` pe `arg_parser` pentru a prelua valorile argumentelor.

Aici, stocăm spațiul de nume al argumentelor în variabila `args`. Astfel, poți utiliza `args.nume_argument` pentru a obține valorile argumentelor.

După ce obținem valorile argumentelor, scriem șirul de mesaj cu majuscule și minuscule inversate (folosind metoda `swapcase()` a șirurilor) în fișier.

args = arg_parser.parse_args()

message = args.message
file = args.file

with open(file,'w') as f:
     f.write(message.swapcase())

Reunind totul, iată conținutul fișierului `main.py`:

# main.py

from argparse import ArgumentParser

arg_parser = ArgumentParser()
arg_parser.add_argument('message',help='șir de mesaj')
arg_parser.add_argument('file',help='nume fișier')

args = arg_parser.parse_args()
print(args)

message = args.message
file = args.file

with open(file,'w') as f:
     f.write(message.swapcase())

Înțelegerea utilizării argumentelor liniei de comandă

Pentru a înțelege cum să folosești argumentele când rulezi `main.py`, folosește opțiunea `–help` (lungă), după cum urmează:

$ python3 main.py --help
usage: main.py [-h] message file

positional arguments:
  message     șir de mesaj
  file        nume fișier

optional arguments:
  -h, --help  afişează acest mesaj de ajutor şi iese

Nu există argumente opționale, iar `message` și `file` sunt argumente poziționale obligatorii. Alternativ, poți folosi opțiunea scurtă `-h`:

$ python3 main.py -h
usage: main.py [-h] message file

positional arguments:
  message     șir de mesaj
  file        nume fișier

optional arguments:
  -h, --help  afişează acest mesaj de ajutor şi iese

Așa cum se observă, ambele argumente sunt implicit poziționale. Dacă nu introduci unul sau ambele argumente, vei întâmpina erori.

Aici, am transmis un argument pozițional („Bună ziua”) pentru șirul de mesaj, dar nu am oferit nicio valoare pentru argumentul fișierului.

Eroarea indică faptul că argumentul fișierului este necesar.

$ python3 main.py Bună ziua
usage: main.py [-h] message file
main.py: error: the following arguments are required: file

Când executăm `main.py` cu ambele argumente poziționale, vedem că spațiul de nume `args` conține valorile argumentelor.

$ python3 main.py Bună ziua file1.txt
# Ieșire
Namespace(file="file1.txt", message="Bună ziua")

Examinând conținutul directorului de lucru, observăm că scriptul creează fișierul `file1.txt`:

$ ls
file1.txt  main.py

Șirul de mesaj original este „Bună ziua”; după inversarea majusculelor și minusculor, conținutul fișierului `file1.txt` este „bUNĂ zIUA”.

$ cat file1.txt
bUNĂ zIUA

Cum să faci opționale argumentele liniei de comandă

Pentru a face argumentele opționale, adaugă un prefix „–” la numele argumentului.

Să modificăm `main.py` pentru a face atât argumentul `message`, cât și argumentul `file` opționale.

# main.py

from argparse import ArgumentParser

arg_parser = ArgumentParser()
arg_parser.add_argument('--message',help='șir de mesaj')
arg_parser.add_argument('--file',help='nume fișier')

Întrucât argumentele liniei de comandă sunt acum opționale, putem stabili valori implicite pentru acestea.

if args.message and args.file:
    message = args.message
    file = args.file
else:
    message="Python3"
    file="myfile.txt"

În acest moment, fișierul `main.py` conține următorul cod:

# main.py

from argparse import ArgumentParser

arg_parser = ArgumentParser()
arg_parser.add_argument('--message',help='șir de mesaj')
arg_parser.add_argument('--file',help='nume fișier')

args = arg_parser.parse_args()
print(args)

if args.message and args.file:
    message = args.message
    file = args.file
else:
    message="Python3"
    file="myfile.txt"

with open(file,'w') as f:
     f.write(message.swapcase())

Dacă verificăm utilizarea, vom observa că ambele argumente sunt opționale. Astfel, poți rula `main.py` fără să oferi niciunul dintre argumente.

$ python3 main.py --help
usage: main.py [-h] [--message MESSAGE] [--file FILE]

optional arguments:
  -h, --help         afişează acest mesaj de ajutor şi iese
  --message MESSAGE  șir de mesaj
  --file FILE        nume fișier
$ python3 main.py

În spațiul de nume al argumentelor, ambele valori pentru `file` și `message` sunt `None`.

# Ieșire
Namespace(file=None, message=None)

Observăm că se utilizează numele implicit al fișierului, „myfile.txt”, și mesajul implicit „Python3”. Fișierul „myfile.txt” este acum prezent în directorul de lucru:

$ ls
file1.txt  main.py  myfile.txt

Și conține șirul „Python3” cu majusculele și minusculele inversate:

$ cat myfile.txt
pYTHON3

Poți folosi argumentele `–message` și `–file` pentru a face comanda mai clară.

$ python3 main.py --message Coding --file file2.txt
# Ieșire
Namespace(file="file2.txt", message="Coding")

Observăm `file2.txt` în directorul de lucru:

$ ls
file1.txt  file2.txt  main.py  myfile.txt

Și conține șirul „coding”, așa cum ne-am așteptat.

$ cat file2.txt
cODING

Concluzie

Iată un rezumat al aspectelor învățate în acest ghid:

  • Similar limbajului C, în Python, poți accesa argumentele din linia de comandă iterând prin vectorul de argumente `sys.argv`. `sys.argv[0]` este numele scriptului Python. De aceea, ne concentrăm pe analiza argumentelor din `sys.argv[1:]`.
  • Pentru a îmbunătăți lizibilitatea și pentru a adăuga opțiuni, folosește modulele `getopt` și `argparse`.
  • Modulul `getopt` analizează lista de argumente din linia de comandă începând de la indexul 1 până la final. Poți defini atât opțiuni scurte, cât și lungi.
  • Când o opțiune necesită un argument, specifică două puncte (:) și, respectiv, egal (=) după opțiunea scurtă și lungă.
  • Cu modulul `argparse` din Python, poți crea un obiect `ArgumentParser` și folosi metoda `add_argument()` pentru a adăuga un argument pozițional obligatoriu. Folosește prefixul „–” în fața numelui argumentului pentru a-l face opțional.
  • Pentru a prelua valorile argumentelor din linia de comandă, apelează metoda `parse_args()` a obiectului `ArgumentParser`.

În continuare, explorează cum să efectuezi hashing securizat în Python.