29. Interagire con il sistema operativo: sys e os
I programmi Python non esistono in isolamento: vengono eseguiti su un sistema operativo che gestisce file, directory, impostazioni dell’ambiente e il modo in cui i programmi si avviano e comunicano. I moduli sys e os forniscono strumenti per interagire con questo ambiente, permettendo ai tuoi programmi di accettare argomenti da riga di comando, leggere variabili d’ambiente, navigare nel file system e creare o rimuovere directory.
Comprendere questi moduli trasforma i tuoi script Python da codice isolato a programmi in grado di integrarsi con il sistema più ampio, rispondere all’input dell’utente all’avvio e gestire file e cartelle in modo programmatico.
29.1) Argomenti da riga di comando e uso di sys.argv
Quando esegui uno script Python dal terminale o dal prompt dei comandi, puoi passargli informazioni aggiuntive: si chiamano argomenti da riga di comando. Per esempio:
python greet.py Alice 25Qui greet.py è il nome dello script, e Alice e 25 sono argomenti passati al programma. Il modulo sys fornisce accesso a questi argomenti tramite sys.argv, una lista(list) che contiene il nome dello script e tutti gli argomenti come stringhe.
29.1.1) Che cos’è sys.argv?
sys.argv è una lista(list) in cui:
sys.argv[0]è sempre il nome dello script in esecuzionesys.argv[1],sys.argv[2], ecc. sono gli argomenti passati dopo il nome dello script- Tutti gli elementi sono stringhe, anche se sembrano numeri
Creiamo uno script semplice per vedere come funziona:
# show_args.py
import sys
print("Script name:", sys.argv[0])
print("Number of arguments:", len(sys.argv))
print("All arguments:", sys.argv)Se esegui questo script con:
python show_args.py hello world 123Output:
Script name: show_args.py
Number of arguments: 4
All arguments: ['show_args.py', 'hello', 'world', '123']Nota che sys.argv contiene 4 elementi: il nome dello script più tre argomenti. Il numero 123 è memorizzato come stringa '123', non come intero.
29.1.2) Usare gli argomenti da riga di comando nei programmi
Gli argomenti da riga di comando permettono agli utenti di personalizzare il comportamento del programma senza modificare il codice. Ecco un programma di saluto che usa il primo argomento come nome:
# greet.py
import sys
if len(sys.argv) < 2:
print("Usage: python greet.py <name>")
sys.exit(1) # Esce con un codice di errore
name = sys.argv[1]
print(f"Hello, {name}!")Eseguendolo:
python greet.py AliceOutput:
Hello, Alice!Se dimentichi di fornire un nome:
python greet.pyOutput:
Usage: python greet.py <name>Il programma controlla se sono stati forniti abbastanza argomenti. In caso contrario, stampa un messaggio d’uso ed esce con sys.exit(1). Il numero 1 è un codice di uscita: per convenzione, 0 significa successo e valori diversi da zero indicano errori. Questo aiuta altri programmi o script a rilevare se il tuo programma è stato eseguito correttamente.
29.1.3) Convertire gli argomenti in altri tipi
Poiché tutti gli argomenti arrivano come stringhe, spesso devi convertirli. Ecco un programma che calcola l’area di un rettangolo:
# area.py
import sys
if len(sys.argv) < 3:
print("Usage: python area.py <width> <height>")
sys.exit(1)
try:
width = float(sys.argv[1])
height = float(sys.argv[2])
except ValueError:
print("Error: Width and height must be numbers")
sys.exit(1)
area = width * height
print(f"Area: {area}")Eseguendolo:
python area.py 5.5 3.2Output:
Area: 17.6Se l’utente fornisce un input non valido:
python area.py five threeOutput:
Error: Width and height must be numbersIl blocco try-except (dal Capitolo 25) gestisce in modo elegante gli errori di conversione, fornendo un feedback utile invece di andare in crash con un traceback.
29.2) Ottenere informazioni sull’interprete e sul runtime con sys
Il modulo sys fornisce informazioni sull’interprete Python stesso e sull’ambiente di runtime. Questo è utile per il debugging, il logging o per scrivere codice che si adatta a diverse versioni di Python o piattaforme.
29.2.1) Informazioni sulla versione di Python
sys.version e sys.version_info ti dicono quale versione di Python sta eseguendo il tuo codice:
import sys
print("Python version string:", sys.version)
print("Version info:", sys.version_info)
print(f"Major version: {sys.version_info.major}")
print(f"Minor version: {sys.version_info.minor}")Output (esempio):
Python version string: 3.11.4 (main, Jul 5 2023, 13:45:01) [GCC 11.2.0]
Version info: sys.version_info(major=3, minor=11, micro=4, releaselevel='final', serial=0)
Major version: 3
Minor version: 11sys.version è una stringa leggibile dall’uomo, mentre sys.version_info è una named tuple che puoi confrontare a livello programmatico:
import sys
if sys.version_info < (3, 8):
print("This program requires Python 3.8 or higher")
sys.exit(1)
print("Python version is compatible")Questo assicura che il tuo programma venga eseguito solo su versioni di Python supportate.
29.2.2) Informazioni sulla piattaforma
sys.platform identifica il sistema operativo:
import sys
print("Platform:", sys.platform)
if sys.platform == "win32":
print("Running on Windows")
elif sys.platform == "darwin":
print("Running on macOS")
elif sys.platform.startswith("linux"):
print("Running on Linux")
else:
print("Running on another platform")Output (su Linux):
Platform: linux
Running on LinuxQuesto ti permette di scrivere codice specifico per piattaforma quando necessario, ad esempio usando percorsi di file diversi o comandi di sistema differenti.
29.2.3) Percorso di ricerca dei moduli
sys.path è una lista(list) di directory in cui Python cerca quando importi moduli:
import sys
print("Module search paths:")
for path in sys.path:
print(f" {path}")Output (esempio):
Module search paths:
/home/user/projects
/usr/lib/python3.11
/usr/lib/python3.11/lib-dynload
/home/user/.local/lib/python3.11/site-packagesLa prima voce è di solito la directory che contiene il tuo script. Python cerca in questi percorsi in ordine quando usi import. Comprendere sys.path aiuta a fare debugging degli errori di import o ad aggiungere directory di moduli personalizzate.
29.2.4) Uscire dai programmi con codici di uscita
Abbiamo visto sys.exit() usato per fermare i programmi. Puoi passare un codice di uscita per indicare successo o fallimento:
import sys
def process_data(filename):
try:
with open(filename) as f:
data = f.read()
# Elabora i dati...
return True
except FileNotFoundError:
print(f"Error: File '{filename}' not found", file=sys.stderr)
return False
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python script.py <filename>", file=sys.stderr)
sys.exit(1)
success = process_data(sys.argv[1])
sys.exit(0 if success else 1)I codici di uscita seguono le convenzioni Unix:
0significa successo- Valori diversi da zero indicano diversi tipi di errori
- Altri programmi possono controllare questi codici per determinare se il tuo programma ha avuto successo
29.3) Accedere alle variabili d’ambiente con os
Le variabili d’ambiente sono coppie chiave-valore impostate dal sistema operativo o dall’utente che i programmi possono leggere. Sono usate per la configurazione, per memorizzare percorsi, chiavi API e altre impostazioni che non dovrebbero essere hardcoded.
29.3.1) Leggere le variabili d’ambiente
Il modulo os fornisce os.environ, un oggetto simile a un dizionario che contiene tutte le variabili d’ambiente:
import os
# Ottiene una specifica variabile d’ambiente
home = os.environ.get('HOME') # On Unix/Linux/macOS
print(f"Home directory: {home}")
# Restituisce None se la variabile non esiste
api_key = os.environ.get('MY_API_KEY')
print(f"API key: {api_key}")Output (su Linux):
Home directory: /home/alice
API key: NonePuoi fornire un valore predefinito:
import os
# Ottiene una variabile d’ambiente con valore predefinito
log_level = os.environ.get('LOG_LEVEL', 'INFO')
print(f"Log level: {log_level}")Output (se LOG_LEVEL non è impostata):
Log level: INFO29.3.2) Variabili d’ambiente comuni
Sistemi operativi diversi forniscono variabili d’ambiente standard:
import os
# Variabili cross-platform
print("Path:", os.environ.get('PATH'))
# Unix/Linux/macOS
print("User:", os.environ.get('USER'))
print("Home:", os.environ.get('HOME'))
print("Shell:", os.environ.get('SHELL'))
# Windows
print("User", os.environ.get('USERNAME'))
print("User Profile:", os.environ.get('USERPROFILE'))
print("Temp:", os.environ.get('TEMP'))PATH è particolarmente importante: elenca le directory in cui il sistema cerca i programmi eseguibili. Quando digiti un comando come python, il sistema cerca in queste directory.
29.3.3) Impostare variabili d’ambiente
Puoi modificare le variabili d’ambiente per il tuo programma e per eventuali sottoprocessi che crea:
import os
# Imposta una variabile d’ambiente
os.environ['MY_CONFIG'] = 'production'
# La rilegge
print(os.environ.get('MY_CONFIG')) # Output: production
# Elimina una variabile d’ambiente
del os.environ['MY_CONFIG']Importante: le modifiche a os.environ influenzano solo l’attuale processo Python e qualsiasi programma che esso avvia. Non persistono dopo l’uscita del programma né influenzano altri programmi.
29.4) Lavorare con percorsi di file e directory (os.path, os.getcwd)
Gestire correttamente i percorsi dei file è cruciale per i programmi che lavorano con i file. I moduli os e os.path forniscono strumenti per costruire, manipolare e interrogare percorsi in modo indipendente dalla piattaforma.
29.4.1) Ottenere la directory di lavoro corrente
La directory di lavoro corrente (CWD) è la cartella che il programma considera come punto di partenza per i percorsi relativi:
import os
cwd = os.getcwd()
print(f"Current working directory: {cwd}")Output (esempio):
Current working directory: /home/alice/projects/myappQuando apri un file con un percorso relativo come 'data.txt', Python lo cerca nella directory di lavoro corrente. Comprendere la CWD aiuta a fare debugging degli errori “file not found”.
29.4.2) Cambiare la directory corrente
Puoi cambiare la directory di lavoro con os.chdir():
import os
original = os.getcwd()
print("Original directory:", original)
# Cambia in una directory diversa
os.chdir('/tmp')
print("New directory:", os.getcwd())
# Torna indietro
os.chdir(original)
print("Back to:", os.getcwd())Output:
Original directory: /home/alice/projects
New directory: /tmp
Back to: /home/alice/projectsNota: Nel Capitolo 28 hai imparato contextlib.chdir(), che ripristina automaticamente la directory originale. Per cambi di directory semplici, preferisci usare il context manager:
from contextlib import chdir
with chdir('/tmp'):
print("Temporarily in:", os.getcwd())
# Automatically restoredQuesto assicura che la directory venga sempre ripristinata, anche se si verifica un errore.
29.4.3) Costruire percorsi con os.path.join()
Sistemi operativi diversi usano separatori di percorso diversi:
- Unix/Linux/macOS:
/(barra) - Windows:
\(backslash)
os.path.join() costruisce i percorsi correttamente per la piattaforma corrente:
import os
# Costruisce un percorso verso un file in una sottodirectory
data_dir = 'data'
filename = 'users.txt'
filepath = os.path.join(data_dir, filename)
print(f"File path: {filepath}")Output (su Unix/Linux/macOS):
File path: data/users.txtOutput (su Windows):
File path: data\users.txtPuoi unire più componenti:
import os
base = '/home/alice'
project = 'myapp'
subdir = 'data'
file = 'config.json'
full_path = os.path.join(base, project, subdir, file)
print(full_path) # Output: /home/alice/myapp/data/config.jsonUsare os.path.join() rende il tuo codice portabile tra sistemi operativi.
29.4.4) Verificare se i percorsi esistono
Prima di lavorare con file o directory, verifica che esistano:
import os
path = 'data.txt'
if os.path.exists(path):
print(f"'{path}' exists")
else:
print(f"'{path}' does not exist")Puoi anche verificare in modo specifico se si tratta di file o directory:
import os
path = 'mydir'
if os.path.isfile(path):
print(f"'{path}' is a file")
elif os.path.isdir(path):
print(f"'{path}' is a directory")
elif os.path.exists(path):
print(f"'{path}' exists but is neither a file nor directory")
else:
print(f"'{path}' does not exist")Questi controlli prevengono errori quando provi ad aprire file inesistenti o a elencare directory inesistenti.
29.4.5) Ottenere percorsi assoluti
os.path.abspath() converte i percorsi relativi in percorsi assoluti:
import os
relative_path = 'data/users.txt'
absolute_path = os.path.abspath(relative_path)
print(f"Relative: {relative_path}")
print(f"Absolute: {absolute_path}")Output (esempio):
Relative: data/users.txt
Absolute: /home/alice/projects/myapp/data/users.txtQuesto è utile per il logging, i messaggi di errore o quando devi conoscere la posizione esatta di un file.
29.4.6) Suddividere i percorsi in componenti
os.path.split() separa un percorso in directory e nome file:
import os
path = '/home/alice/projects/data.txt'
directory, filename = os.path.split(path)
print(f"Directory: {directory}")
print(f"Filename: {filename}")Output:
Directory: /home/alice/projects
Filename: data.txtos.path.basename() ottiene solo il nome file, e os.path.dirname() ottiene solo la directory:
import os
path = '/home/alice/projects/data.txt'
print(f"Basename: {os.path.basename(path)}") # Output: data.txt
print(f"Dirname: {os.path.dirname(path)}") # Output: /home/alice/projects29.4.7) Suddividere le estensioni dei file
os.path.splitext() separa il nome del file dalla sua estensione:
import os
filename = 'report.pdf'
name, extension = os.path.splitext(filename)
print(f"Name: {name}") # Output: report
print(f"Extension: {extension}") # Output: .pdfQuesto è utile per elaborare file in base al loro tipo:
import os
files = ['data.csv', 'image.png', 'document.txt', 'script.py']
for file in files:
name, ext = os.path.splitext(file)
if ext == '.csv':
print(f"Process CSV file: {file}")
elif ext == '.png':
print(f"Process image file: {file}")Output:
Process CSV file: data.csv
Process image file: image.png29.5) Elencare, creare e rimuovere file e directory
Il modulo os fornisce funzioni per manipolare il file system: elencare il contenuto delle directory, creare nuove directory e rimuovere file e cartelle.
29.5.1) Elencare il contenuto di una directory
os.listdir() restituisce una lista(list) di tutti gli elementi in una directory:
import os
# Elenca il contenuto della directory corrente
contents = os.listdir('.')
print("Current directory contents:")
for item in contents:
print(f" {item}")Output (esempio):
Current directory contents:
script.py
data.txt
mydir
README.mdLa lista(list) include sia file sia directory. Per distinguerli:
import os
contents = os.listdir('.')
print("Files:")
for item in contents:
if os.path.isfile(item):
print(f" {item}")
print("\nDirectories:")
for item in contents:
if os.path.isdir(item):
print(f" {item}")Output:
Files:
script.py
data.txt
README.md
Directories:
mydir29.5.2) Creare directory
os.mkdir() crea una singola directory:
import os
new_dir = 'output'
if not os.path.exists(new_dir):
os.mkdir(new_dir)
print(f"Created directory: {new_dir}")
else:
print(f"Directory already exists: {new_dir}")Output:
Created directory: outputImportante: os.mkdir() fallisce se la directory padre non esiste. Per esempio, provare a creare 'data/output' quando 'data' non esiste genererà un errore.
os.makedirs() crea tutte le directory padre necessarie:
import os
nested_dir = 'data/processed/2024'
if not os.path.exists(nested_dir):
os.makedirs(nested_dir)
print(f"Created directory structure: {nested_dir}")Output:
Created directory structure: data/processed/2024Questo crea data, poi data/processed, poi data/processed/2024 se non esistono.
29.5.3) Rimuovere file
os.remove() elimina un file:
import os
filename = 'temp.txt'
# Crea un file temporaneo
with open(filename, 'w') as f:
f.write('Temporary data')
print(f"File exists: {os.path.exists(filename)}") # Output: True
# Rimuove il file
os.remove(filename)
print(f"File exists: {os.path.exists(filename)}") # Output: FalseAvvertenza: os.remove() elimina i file in modo permanente: non vanno nel cestino.
29.5.4) Rimuovere directory
os.rmdir() rimuove una directory vuota:
import os
directory = 'empty_dir'
# Crea e poi rimuove una directory vuota
os.mkdir(directory)
print(f"Created: {directory}")
os.rmdir(directory)
print(f"Removed: {directory}")os.rmdir() fallisce se la directory contiene file. Per rimuovere una directory e tutto il suo contenuto, devi prima eliminare i file:
import os
def remove_directory_contents(directory):
"""Rimuove tutti i file in una directory, poi rimuove la directory.
Nota: fallisce se la directory contiene sottodirectory.
"""
if not os.path.exists(directory):
print(f"Directory does not exist: {directory}")
return
# Rimuove tutti i file nella directory
for item in os.listdir(directory):
item_path = os.path.join(directory, item)
if os.path.isfile(item_path):
os.remove(item_path)
print(f"Removed file: {item_path}")
# Rimuove la directory ora vuota
os.rmdir(directory)
print(f"Removed directory: {directory}")
# Esempio di utilizzo
test_dir = 'test_data'
os.makedirs(test_dir, exist_ok=True)
# Crea alcuni file di test
with open(os.path.join(test_dir, 'file1.txt'), 'w') as f:
f.write('test')
with open(os.path.join(test_dir, 'file2.txt'), 'w') as f:
f.write('test')
# Rimuove tutto
remove_directory_contents(test_dir)Output:
Removed file: test_data/file1.txt
Removed file: test_data/file2.txt
Removed directory: test_dataNota: Per una rimozione di directory più complessa (incluse le sottodirectory), il modulo shutil di Python fornisce shutil.rmtree(), ma questo va oltre il nostro ambito attuale.
29.5.5) Rinominare file e directory
os.rename() rinomina o sposta file e directory:
import os
# Rinomina un file
old_name = 'draft.txt'
new_name = 'final.txt'
# Crea il file di test
with open(old_name, 'w') as f:
f.write('content')
os.rename(old_name, new_name)
print(f"Renamed '{old_name}' to '{new_name}'")Puoi anche spostare i file in directory diverse:
import os
# Crea directory e file
os.makedirs('source', exist_ok=True)
os.makedirs('destination', exist_ok=True)
with open('source/file.txt', 'w') as f:
f.write('content')
# Sposta il file in una directory diversa
os.rename('source/file.txt', 'destination/file.txt')
print("Moved file to destination directory")I moduli sys e os danno ai tuoi programmi Python la capacità di interagire con il sistema operativo, accettare input da riga di comando, leggere la configurazione e gestire file e directory. Queste capacità trasformano script semplici in potenti strumenti da riga di comando che si integrano senza problemi con l’ambiente di sistema più ampio.