Padroneggiare i Parametri Posizionali: Una Guida agli Argomenti degli Script Bash

Scatena la potenza degli script Bash dinamici padroneggiando i parametri posizionali. Questa guida completa spiega come accedere agli argomenti della riga di comando usando `$1`, `$2` e variabili speciali come `$#` (conteggio argomenti) e il cruciale `"$@"` (tutti gli argomenti). Impara le migliori pratiche essenziali per la validazione dell'input, comprendi la differenza tra `$*` e `$@`, e vedi esempi pratici per scrivere script robusti e con controllo degli errori che si adattano perfettamente all'input dell'utente.

Padroneggiare i Parametri Posizionali: Una Guida agli Argomenti degli Script Bash

Gli script Bash diventano molto più utili quando accettano argomenti invece di obbligarti a modificare le variabili all'interno del file. Uno script di backup dovrebbe accettare una directory di origine. Uno script di deploy dovrebbe accettare un nome di ambiente. Uno script di pulizia dovrebbe accettare uno o più percorsi. Questi valori arrivano come parametri posizionali: $1, $2, $3 e così via.

La parte difficile non è leggere $1. La parte difficile è gestire argomenti mancanti, argomenti con spazi, flag opzionali e il momento in cui il tuo script passa da "solo per me" a qualcosa che un'altra persona eseguirà alle 2 del mattino.


L'Anatomia dei Parametri Posizionali

I parametri posizionali sono variabili speciali definite dalla shell che corrispondono alle parole fornite sulla riga di comando dopo il nome dello script. Sono numerati in sequenza, a partire da 1.

Parametro Descrizione Valore di Esempio (eseguendo ./script.sh file1 dir/)
$0 Il nome dello script stesso (o della funzione). ./script.sh
$1 Il primo argomento passato allo script. file1
$2 Il secondo argomento passato allo script. dir/
$N L'N-esimo argomento (dove N > 0).
${10} Gli argomenti oltre il 9 devono essere racchiusi tra parentesi graffe.

Accesso agli Argomenti Oltre $9

Mentre gli argomenti da 1 a 9 sono accessibili direttamente come $1 fino a $9, per accedere al decimo argomento e successivi è necessario racchiudere il numero tra parentesi graffe per evitare ambiguità con variabili d'ambiente o operazioni sulle stringhe (ad esempio, ${10} invece di $10).


Parametri Speciali Essenziali per gli Script

Oltre ai parametri numerici, Bash fornisce diverse variabili speciali critiche che si riferiscono all'insieme di argomenti nel suo complesso. Queste sono indispensabili per la validazione e l'iterazione.

Contare gli Argomenti con $#

La variabile speciale $# contiene il numero totale di argomenti della riga di comando passati allo script (escluso $0). Questa è forse la variabile più importante per implementare la validazione dell'input.

#!/bin/bash

if [ "$#" -eq 0 ]; then
    echo "Errore: Nessun argomento fornito."
    echo "Utilizzo: $0 <file_input>"
    exit 1
fi

echo "Hai fornito $# argomenti."

Tutti gli Argomenti: $@ e $*

Le variabili $@ e $* rappresentano entrambe l'elenco completo degli argomenti, ma si comportano diversamente, specialmente quando sono racchiuse tra virgolette.

$* (Stringa Singola)

Quando è racchiusa tra virgolette doppie ("$*"), l'intero elenco di parametri posizionali viene trattato come un unico argomento, separato dal primo carattere della variabile IFS (Internal Field Separator, solitamente uno spazio).

  • Se gli argomenti di input sono: arg1 arg2 arg3
  • "$*" si espande in: "arg1 arg2 arg3" (un unico elemento)

$@ (Stringhe Separate - Preferito)

Quando è racchiusa tra virgolette doppie ("$@"), ogni parametro posizionale viene trattato come un argomento separato e quotato. Questo è il metodo standard e preferito per iterare sugli argomenti, poiché preserva correttamente gli argomenti contenenti spazi.

  • Se gli argomenti di input sono: arg1 "arg con spazio" arg3
  • "$@" si espande in: "arg1" "arg con spazio" "arg3" (tre elementi distinti)

Perché le Virgolette Contano: Una Dimostrazione

Considera uno script eseguito con gli argomenti: ./test.sh 'ciao mondo' file.txt

#!/bin/bash

# $* senza virgolette divide sugli spazi e di solito è sbagliato.
echo "-- Iterazione usando $* senza virgolette --"
for item in $*; do
    echo "Elemento: $item"
done

# "$@" con virgolette preserva ogni argomento originale.
echo "-- Iterazione usando "$@" con virgolette --"
for item in "$@"; do
    echo "Elemento: $item"
done

Con ./test.sh 'ciao mondo' file.txt, il ciclo senza virgolette stampa ciao e mondo come elementi separati. Il ciclo con "$@" mantiene ciao mondo come un unico argomento. Questa differenza è il motivo per cui gli utenti esperti di shell usano "$@" quasi automaticamente.


Tecniche Pratiche per la Gestione degli Argomenti

1. Script di Base per il Recupero degli Argomenti

Questo semplice script dimostra come accedere a parametri specifici e usare $0 per fornire un feedback utile.

deploy_service.sh:

#!/bin/bash
# Utilizzo: deploy_service.sh <nome_servizio> <ambiente>

SERVICE_NAME="$1"
ENVIRONMENT="$2"

# Controllo di validazione (minimo due argomenti)
if [ "$#" -lt 2 ]; then
    echo "Utilizzo: $0 <nome_servizio> <ambiente>"
    exit 1
fi

echo "Avvio del deployment per il servizio: $SERVICE_NAME"
echo "Ambiente di destinazione: $ENVIRONMENT"

# Esegui il comando usando i parametri validati
ssh admin@server-"$ENVIRONMENT" "/path/to/start $SERVICE_NAME"

2. Validazione Robusta dell'Input

I buoni script validano sempre l'input prima di procedere. Questo include il controllo del conteggio ($#) e spesso il controllo del contenuto degli argomenti (ad esempio, verificare se un argomento è un numero o un percorso file valido).

#!/bin/bash

# 1. Controlla il Conteggio degli Argomenti (Deve essere esattamente 3)
if [ "$#" -ne 3 ]; then
    echo "Errore: Questo script richiede tre argomenti (origine, destinazione, utente)."
    echo "Utilizzo: $0 <percorso_src> <percorso_dest> <utente>"
    exit 1
fi

SRC_PATH="$1"
DEST_PATH="$2"
USER="$3"

# 2. Controlla il Contenuto (Esempio: Verifica che il percorso di origine esista)
if [ ! -f "$SRC_PATH" ]; then
    echo "Errore: Il file di origine '$SRC_PATH' non trovato o non è un file."
    exit 2
fi

# Se la validazione passa, procedi
echo "Copia di $SRC_PATH in $DEST_PATH come utente $USER..."

Suggerimento sulle Migliori Pratiche: Fornisci sempre un'istruzione Utilizzo: chiara e concisa quando la validazione fallisce. Questo aiuta gli utenti a correggere rapidamente l'invocazione del comando.

3. Iterare sugli Argomenti con shift

Il comando shift è uno strumento eccellente per elaborare gli argomenti in sequenza, spesso usato quando si gestiscono flag semplici o quando si elaborano gli argomenti uno per uno all'interno di un ciclo while.

shift scarta l'argomento corrente $1, sposta $2 a $1, $3 a $2 e decrementa $# di uno. Questo ti permette di elaborare il primo argomento e poi continuare il ciclo finché non rimangono più argomenti.

#!/bin/bash

# Elabora un semplice flag -v e poi elenca i file rimanenti

VERBOSE=false

if [ "$1" = "-v" ]; then
    VERBOSE=true
    shift  # Scarta il flag -v e sposta gli argomenti in su
fi

if $VERBOSE; then
    echo "Modalità verbosa abilitata."
fi

if [ "$#" -eq 0 ]; then
    echo "Nessun file specificato."
    exit 0
fi

echo "Elaborazione di $# file rimanenti:"
for file in "$@"; do
    if $VERBOSE; then
        echo "Controllo del file: $file"
    fi
    # ... logica di elaborazione qui
done

Nota: shift è utile per un'analisi semplice. Per script complessi con molti flag, getopts è solitamente più adatto per le opzioni brevi. La gestione delle opzioni lunghe varia a seconda della piattaforma, quindi testa attentamente se usi getopt esterno.

Un Analizzatore Più Realistico

Molti script interni iniziano con un flag opzionale e un valore obbligatorio. Ecco un piccolo schema che rimane leggibile:

#!/usr/bin/env bash
set -u

dry_run=false
environment=""

usage() {
    echo "Utilizzo: $0 [--dry-run] --env <dev|staging|prod> <file>..." >&2
}

while [ "$#" -gt 0 ]; do
    case "$1" in
        --dry-run)
            dry_run=true
            shift
            ;;
        --env)
            if [ "$#" -lt 2 ]; then
                echo "Errore: --env richiede un valore." >&2
                usage
                exit 2
            fi
            environment="$2"
            shift 2
            ;;
        --help|-h)
            usage
            exit 0
            ;;
        --)
            shift
            break
            ;;
        -*)
            echo "Errore: opzione sconosciuta: $1" >&2
            usage
            exit 2
            ;;
        *)
            break
            ;;
    esac
done

if [ -z "$environment" ]; then
    echo "Errore: --env è obbligatorio." >&2
    usage
    exit 2
fi

if [ "$#" -eq 0 ]; then
    echo "Errore: fornisci almeno un file." >&2
    usage
    exit 2
fi

for file in "$@"; do
    if [ ! -f "$file" ]; then
        echo "Errore: file non trovato: $file" >&2
        exit 3
    fi

    if $dry_run; then
        echo "Caricherei $file in $environment"
    else
        echo "Caricamento di $file in $environment"
        # comando di upload qui
    fi
done

Nota i dettagli noiosi. I messaggi di errore vanno su stderr. -- significa "interrompi l'analisi delle opzioni", il che permette a qualcuno di passare un nome file che inizia con un trattino. Il ciclo finale sui file usa "$@", quindi note di rilascio.txt rimane un unico nome file.

Errori Comuni

L'errore più comune è dimenticare le virgolette:

cp $1 $2

Questo si rompe quando uno dei percorsi contiene spazi o caratteri glob della shell. Usa:

cp -- "$1" "$2"

Il -- dice a molti comandi che l'analisi delle opzioni è terminata, il che aiuta se un percorso inizia con -.

Un altro errore comune è validare troppo tardi. Se il tuo script si aspetta due argomenti, controllalo prima di fare qualsiasi cosa distruttiva:

if [ "$#" -ne 2 ]; then
    echo "Utilizzo: $0 <sorgente> <destinazione>" >&2
    exit 2
fi

Usa codici di uscita distinti quando aiuta il chiamante. Un errore di utilizzo potrebbe essere 2; un file mancante potrebbe essere 3; un comando esterno fallito può mantenere il proprio stato. Non hai bisogno di una tassonomia enorme di codici di uscita, ma restituire 0 dopo un'invocazione errata rende l'automazione meno affidabile.

Le Funzioni Hanno Anche Loro Parametri Posizionali

All'interno di una funzione Bash, $1 e $2 si riferiscono agli argomenti della funzione, non agli argomenti originali dello script.

log_copy() {
    local src="$1"
    local dest="$2"

    echo "Copia di $src in $dest"
    cp -- "$src" "$dest"
}

log_copy "$1" "$2"

Questo è utile, ma può sorprenderti se ti aspettavi che $1 all'interno della funzione significasse il primo argomento a livello di script. Passa i valori esplicitamente. Rende la funzione più facile da testare e più facile da riutilizzare.

Inoltrare Argomenti a un Altro Comando

Molti script wrapper esistono solo per aggiungere un po' di configurazione prima di chiamare un altro comando. In questo caso, "$@" è ciò che mantiene onesto il wrapper.

#!/usr/bin/env bash
set -e

export APP_ENV=staging
exec /usr/local/bin/myapp "$@"

Se qualcuno esegue:

./run-staging.sh --config "config con spazi.yml" --verbose

il comando wrappato riceve gli stessi tre argomenti. Se avessi usato $* o $@ senza virgolette, il percorso di configurazione potrebbe essere suddiviso in più parole.

exec è opzionale, ma è spesso utile nei wrapper perché sostituisce il processo della shell con il processo di destinazione. Questo rende i segnali più prevedibili sotto systemd, Docker o un supervisore di processo.

Valori Predefiniti Senza Sorprese

A volte un argomento dovrebbe essere opzionale. L'espansione dei parametri Bash può aiutare:

environment="${1:-dev}"

Questo significa "usa $1 se è impostato e non vuoto; altrimenti usa dev." Va bene per script locali amichevoli, ma fai attenzione con gli script di produzione. Un valore predefinito silenzioso può fare il deploy nell'ambiente sbagliato se qualcuno dimentica un argomento.

Per comandi rischiosi, preferisci l'input esplicito:

if [ "$#" -lt 1 ]; then
    echo "Utilizzo: $0 <ambiente>" >&2
    exit 2
fi

I valori predefiniti sono migliori quando la conseguenza è piccola, come impostare un livello di log o una directory di output predefiniti. Sono rischiosi quando l'argomento sceglie un server, cancella dati o cambia un target di deployment.

Parametri Posizionali e set -u

Molti script Bash usano set -u in modo che le variabili non impostate causino un errore. Questo può cogliere i refusi, ma cambia anche il modo in cui si comportano i parametri posizionali mancanti.

#!/usr/bin/env bash
set -u

echo "Primo argomento: $1"

Esegui quello script senza argomenti e Bash esce con un errore "variabile non vincolata". Quell'errore è tecnicamente corretto, ma non è amichevole. Valida $# prima di leggere i parametri richiesti:

if [ "$#" -lt 1 ]; then
    echo "Utilizzo: $0 <file-input>" >&2
    exit 2
fi

input_file="$1"

Per parametri opzionali sotto set -u, usa un'espansione protetta:

mode="${2:-default}"

Questo mantiene la modalità rigorosa utile senza far crashare lo script per valori opzionali mancanti.

Quando i Parametri Posizionali Sono l'Interfaccia Sbagliata

I parametri posizionali sono ottimi per comandi piccoli:

backup.sh /var/www /backup/www.tar.gz

Diventano difficili da leggere quando lo script accetta molti valori:

deploy.sh prod us-east-1 api v2.4.1 true false 30

Nessuno vuole ricordare cosa significa il quinto argomento. Una volta che uno script raggiunge quel punto, usa flag nominativi o un file di configurazione:

deploy.sh --env prod --region us-east-1 --service api --version v2.4.1 --timeout 30

Il codice è leggermente più lungo, ma la riga di comando diventa auto-documentante. Questo è un buon compromesso per gli script usati da un team.

Una buona gestione dei parametri posizionali è principalmente disciplina: validare presto, quotare ogni espansione a meno che non si voglia intenzionalmente la suddivisione, usare "$@" per inoltrare argomenti e mantenere i messaggi di utilizzo vicini ai controlli che li attivano. Queste abitudini fanno sì che i piccoli script sopravvivano a nomi di file reali, utenti reali e automazione reale.