HOWTO · Python

Come eseguire script Bash in Python

Impara a eseguire script Bash utilizzando Python con la nostra guida completa. Esplora metodi come subprocess, os.system e subprocess.Popen per eseguire comandi Bash in modo efficiente. Migliora le tue abilità di automazione e semplifica i flussi di lavoro integrando script Bash nelle tue applicazioni Python.

In questa pagina

Per la maggior parte degli script Bash che devono terminare prima che Python prosegua, usa subprocess.run. Puoi passare gli argomenti separatamente, acquisire stdout e stderr e controllare il returncode del processo. Bash deve essere installato e disponibile per Python: questo approccio dipende quindi dall’ambiente host e non è portabile sui sistemi privi di Bash. L’esempio seguente crea un file di script, passa un nome come argomento e mostra il risultato acquisito.

Metodo 1: usare il modulo subprocess

Questo esempio autonomo crea un piccolo script Bash, gli passa un argomento e acquisisce entrambi i flussi di output. Lo script Bash stampa un saluto; Python stampa poi il testo acquisito e lo stato del processo. Le etichette e il testo mostrati nei blocchi di esempio restano in inglese, poiché tali blocchi vengono mantenuti invariati.

import subprocess
import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as directory:
    script_path = Path(directory) / "greet.sh"
    script_path.write_text(
        '#!/usr/bin/env bash\nprintf "Hello, %s!\\n" "$1"\n',
        encoding="utf-8",
    )

    result = subprocess.run(
        ["bash", str(script_path), "Ada"],
        capture_output=True,
        text=True,
        check=False,
    )

print(result.stdout, end="")
print(f"Return code: {result.returncode}")
Hello, Ada!
Return code: 0

L’elenco degli argomenti mantiene il percorso dello script e Ada come argomenti distinti. Con il valore predefinito shell=False, Python avvia Bash direttamente, senza chiedere a una shell di interpretare l’elenco. In questo modo la shell non interpreta quegli argomenti, ma non è una garanzia di sicurezza universale: convalida gli input e considera anche ciò che fa lo script. Un returncode pari a zero indica il successo; un valore diverso da zero indica che il processo ha segnalato un errore. stderr è un flusso separato per i messaggi diagnostici: la presenza di testo al suo interno non significa, da sola, che il comando sia fallito. capture_output=True rende disponibili sia stdout sia stderr nell’oggetto risultato.

Metodo 2: usare os.system

Usa os.system solo quando basta un comando semplice e Python non deve acquisirne l’output. Accetta un’unica stringa di comando, la esegue tramite una shell di sistema e invia i flussi del comando ai flussi standard del processo circostante. Il valore restituito dipende dal sistema e può codificare lo stato del processo invece di corrispondere al codice di uscita del processo figlio: non considerarlo quindi un codice di uscita diretto e portabile. Non fornisce un oggetto risultato da cui leggere direttamente stdout o stderr. Evita di comporre la stringa del comando usando input non attendibili, perché la shell potrebbe attribuire a quell’input un significato speciale. Per passare argomenti separati, acquisire i flussi e controllare lo stato in modo affidabile, preferisci subprocess.

Metodo 3: usare subprocess.Popen

Scegli subprocess.Popen quando ti serve un controllo più dettagliato su un processo in esecuzione, per esempio per gestirne la durata o collegare pipe. Per uno scambio finito, imposta stdin=subprocess.PIPE quando crei il processo, quindi chiama communicate(input=...) per inviare l’input e acquisire i flussi dopo che il processo è terminato. Senza quella pipe di input, communicate(input=...) non può inviare dati al processo figlio. L’output non viene fornito in tempo reale. Se crei pipe per entrambi i flussi di output, svuotale con communicate(); attendere il processo figlio mentre una pipe si riempie può causare un deadlock. Per trasmettere i dati in tempo reale, leggi e svuota i flussi in modo esplicito invece di trattare communicate() come un’API di streaming. Il comportamento preciso delle pipe dipende dalla piattaforma e dal processo.

Conclusione

Per uno script Bash che deve terminare prima che il programma prosegua, di solito subprocess.run è la scelta più chiara: passa una lista, acquisisci i flussi quando serve e usa returncode per determinare se il processo è riuscito. Riserva os.system ai semplici comandi di shell per cui non serve gestire l’output; usa subprocess.Popen quando occorre controllare o gestire esplicitamente il processo. Verifica che Bash sia installato e disponibile nell’ambiente in cui viene eseguito il programma Python: lo script può essere avviato solo se Python riesce a trovare l’eseguibile Bash.

Domande frequenti

  1. Qual è il modo migliore per eseguire uno script Bash da Python?
    Per la maggior parte degli script che devono terminare prima che Python prosegua, usa subprocess.run. Supporta liste di argomenti, l’acquisizione dei flussi e la verifica dello stato del processo tramite returncode.

  2. Come posso acquisire l’output di uno script?
    Imposta capture_output=True e text=True in subprocess.run, quindi leggi stdout e stderr dall’oggetto restituito. Con Popen, collega le pipe necessarie e chiama communicate() per acquisire i flussi dopo il completamento; questo metodo raccoglie l’output, ma non lo trasmette in tempo reale. stderr può contenere messaggi diagnostici anche quando il processo termina correttamente: controlla returncode per verificarne lo stato.

  3. Passare una lista è sicuro?
    Con shell=False, una lista evita che la shell interpreti gli argomenti forniti. Ciò riduce il rischio di command injection tramite la shell, ma non rende sicuri tutti gli script o tutti gli input: convalida i valori e considera il comportamento implementato dallo script.

  4. Posso passare argomenti a uno script Bash?
    Sì. Aggiungi ciascun argomento come elemento distinto dopo il percorso dello script nella lista di argomenti di subprocess. os.system accetta una sola stringa di comando, quindi non offre lo stesso passaggio strutturato degli argomenti.

  5. Come posso fornire input allo script?
    Per uno scambio semplice e finito, usa subprocess.run con input=...; se ti serve un controllo più dettagliato del processo, usa Popen con stdin=subprocess.PIPE e communicate(input=...). communicate() invia l’input e acquisisce i flussi del processo dopo che quello figlio è terminato.