Corso Linux / Come Bash esegue i comandi: ;, &&, ||, alias e variabili
Bash legge la riga di comando, interpreta nomi e argomenti, applica espansioni e redirezioni e avvia l’esecuzione. Il punto e virgola ; prosegue indipendentemente dal risultato precedente, && prosegue solo dopo uno stato di successo e || dopo uno stato non nullo. Gli alias sostituiscono testo durante la lettura del comando; le variabili d’ambiente trasmettono impostazioni ai processi figli. Queste differenze spiegano perché righe apparentemente simili possono comportarsi diversamente.
Inizia da variabili, ambiente, alias, stati di uscita e uso di base di && e ||. Gruppi, pipeline, lavori in background e script completo sono approfondimenti da leggere quando servono. Le lezioni sul primo script Bash (in inglese) e sulla gestione degli errori in Bash (in inglese) sviluppano rispettivamente la scrittura di un file e i limiti legati a errori, percorsi e concorrenza.
Come viene risolto il nome di un comando?
Gli esempi richiedono Bash. La shell interpreta argomenti, variabili e redirezioni e risolve il nome da eseguire: un alias, una funzione, un comando incorporato nella shell o un eseguibile trovato tramite PATH possono comportarsi diversamente. Per controllare come il nome viene interpretato nella shell corrente, esegui:
type -a printf
type -a ls
command -v ls
type -a può mostrare alias, funzioni, comandi incorporati e candidati eseguibili; command -v verifica come un nome viene risolto. L’output dipende dall’ambiente e non è necessariamente un percorso assoluto. PATH elenca directory in cui cercare programmi esterni, ma non descrive ogni alias o funzione come un unico file. Non sovrascrivere il PATH del sistema per questa esercitazione.
Leggere stati di uscita e simboli di esecuzione
Un comando termina con uno stato di uscita: 0 indica successo, un valore non nullo indica un insuccesso o un altro risultato definito dal programma. Per esempio, grep restituisce 1 se non trova corrispondenze. Leggi subito $? oppure salvalo in una variabile, perché il comando successivo sostituisce lo stato più recente.
true
printf 'status=%s\n' "$?"
false
printf 'status=%s\n' "$?"
L’output è status=0, seguito da status=1. L’esecuzione condizionale dipende dallo stato, non dal fatto che un messaggio sembri positivo. Bash usa 127 quando non trova un comando e 126 quando lo trova ma non può eseguirlo; la terminazione tramite segnali segue altre convenzioni. Non tutti gli insuccessi restituiscono 1.
| Simbolo | Funzione |
|---|---|
; |
Esegue il comando successivo indipendentemente dal risultato |
&& |
Prosegue solo dopo uno stato 0 |
|| |
Prosegue dopo uno stato non nullo |
& |
Avvia senza attendere il completamento |
| |
Collega stdout al successivo stdin |
|& |
Invia stdout e stderr nella pipe |
! |
Nega logicamente lo stato del comando o della pipeline |
Nella tabella, stdout indica l’uscita standard, stdin l’ingresso standard e stderr l’errore standard, cioè il canale diagnostico.
;, &, && e || sono operatori che formano liste di comandi. | e |& formano pipeline, mentre ! è una parola riservata che nega lo stato di una pipeline. Gli esempi usano Bash con true, false, printf, grep, cat e sleep, normalmente disponibili su Ubuntu e Rocky Linux; non servono privilegi amministrativi.
Collegare comandi in base a successo e insuccesso
Sequenza ed esecuzione condizionale
; attende che il comando precedente termini e prosegue qualunque sia il risultato. Anche un fine riga separa i comandi nelle normali liste. Virgolette, barre inverse, pipeline o comandi composti incompleti possono però far proseguire la lettura invece di terminare il comando.
false; printf 'always\n'
false && printf 'not run\n'
false || printf 'fallback\n'
true && printf 'next step\n'
Vengono stampati always, fallback e next step; not run viene saltato. && e || usano la valutazione a cortocircuito: il comando a destra non viene eseguito se la condizione non è soddisfatta. Il modello cd /percorso/di/lavoro && comando_di_lavoro evita di lavorare nella directory sbagliata dopo un cd fallito. È uno schema: sostituisci percorso e comando con valori reali, già preparati.
Ordine di valutazione e rami if
&& e || hanno la stessa precedenza e si associano da sinistra a destra. In A && B || C, C può essere eseguito anche quando A riesce, perché B può fallire.
true && false || printf 'fallback runs\n'
Viene stampato fallback runs. Usa if quando deve essere soltanto la condizione iniziale a scegliere fra due rami.
if true; then
false
else
printf 'condition failed\n'
fi
Non viene stampato nulla: il fallimento di false nel ramo then già scelto non trasferisce l’esecuzione a else. Questo errore richiede comunque una gestione esplicita in uno script operativo: scegliere il ramo corretto non recupera un’operazione fallita.
Segnalare un errore senza perdere lo stato
false || printf 'failure reported\n'
printf 'list status=%s\n' "$?"
L’output è failure reported, poi list status=0. || non significa necessariamente “ignora gli errori”, ma il printf riuscito diventa l’ultimo comando eseguito e assegna stato 0 alla lista. Se lo script deve terminare con un errore dopo averlo segnalato, salva lo stato originale e restituiscilo con exit.
Raggruppare comandi in una sola unità
Le graffe usano la shell corrente
{ ...; } raggruppa comandi nella shell corrente. Separa le parentesi graffe dai comandi con spazi e metti un punto e virgola o un fine riga prima della graffa di chiusura. Per le prove con variabili e alias usa una nuova sessione Bash di esercitazione: se i nomi LESSON_SCOPE, LESSON_MODE, lesson_job o ll sono già usati per altro, non sovrascriverli e scegli nomi liberi in tutti i comandi della relativa prova.
LESSON_SCOPE=outer
{ LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE"; }
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE
Viene stampato inner due volte, perché la modifica della variabile rimane nella shell corrente. L’ultimo unset elimina la variabile di esercitazione.
Le parentesi tonde creano una sottoshell
LESSON_SCOPE=outer
( LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE" )
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE
L’output è inner, poi outer. Le parentesi tonde creano un ambiente di sottoshell, quindi assegnazioni ordinarie e cambi di directory non rimangono nella shell esterna. Questo isolamento non annulla effetti esterni come scrittura di file o richieste a servizi.
Usare pipe, errore standard e negazione dello stato
Distinguere pipe e condizioni
printf 'alpha\nbeta\n' | grep 'alpha'
Viene stampato soltanto alpha. | collega l’uscita standard del comando precedente all’ingresso standard del successivo. Non seleziona un comando dopo un insuccesso come fa ||: i comandi della pipeline elaborano flussi collegati e non sono passi selezionati dal successo del precedente.
Includere stderr con |&
bash -c 'printf "normal\n"; printf "error\n" >&2' |& cat
Sia normal sia error attraversano cat. In questo esempio |& di Bash unisce l’errore standard all’uscita standard come 2>&1 |. In presenza di altre redirezioni conta il loro ordine: non sostituire le forme alla cieca. Un tee a valle può mostrare e salvare il flusso, ma i due tipi di messaggio sono ormai mescolati.
! nega lo stato, non l’output
! false
printf 'inverted status=%s\n' "$?"
Viene stampato inverted status=0: lo stato di insuccesso di false viene negato e diventa successo nella condizione. Uno stato iniziale 0 diventa invece non nullo. Il testo dell’output e il contenuto dei file non vengono invertiti.
Controllare gli errori della pipeline con pipefail
( set +o pipefail; false | cat; printf 'default=%s\n' "$?" )
( set -o pipefail; false | cat; printf 'pipefail=%s\n' "$?" )
L’output è default=0, poi pipefail=1. Normalmente una pipeline restituisce lo stato dell’ultimo comando: un cat riuscito può nascondere il precedente false. Con pipefail lo stato è 0 solo se tutti i comandi riescono; altrimenti proviene dal comando più a destra che ha restituito uno stato non nullo. Le parentesi isolano le due prove e non cambiano le opzioni della shell esterna.
Avviare un lavoro in background e verificarne la conclusione
& avvia un’esecuzione asincrona senza attenderne il completamento. È diverso da &&, che procede solo dopo un successo. Questo esempio avvia un breve lavoro in background e ne attende intenzionalmente la conclusione.
sleep 2 &
lesson_job=$!
printf 'foreground continues\n'
wait "$lesson_job"
printf 'job status=%s\n' "$?"
unset lesson_job
Appare prima foreground continues, poi job status=0 dopo il completamento. $! identifica il processo più recente avviato in background. Il valore di $? immediatamente dopo l’avvio non dimostra che il lavoro finirà bene. In Bash interattivo, jobs, fg e bg permettono di vedere e spostare i lavori. Aggiungere soltanto & non garantisce la sopravvivenza dopo il logout: per lavori lunghi scegli una gestione adeguata della sessione o del servizio.
Provare il flusso con uno script completo
Il seguente esempio completo simula compilazione, test e distribuzione con soli messaggi: non compila, non distribuisce e non elimina file. Crea una directory vuota di esercitazione e salva il contenuto in un file nuovo chiamato course15-flow.sh, con terminatori di riga LF. Se directory o file esistono già, scegli una posizione nuova senza sovrascriverli. Serve solo Bash e non occorre rendere eseguibile il file.
#!/usr/bin/env bash
build() { printf 'build\n'; }
run_tests() { printf 'test\n'; return "${1:-0}"; }
deploy() { printf 'deploy\n'; }
build && run_tests "${1:-0}" && deploy
lesson_status=$?
if (( lesson_status != 0 )); then
printf 'workflow failed: status=%s\n' "$lesson_status" >&2
fi
exit "$lesson_status"
bash course15-flow.sh 0
echo $?
bash course15-flow.sh 1
echo $?
La prima esecuzione stampa build, test e deploy, poi restituisce 0. Con l’argomento 1 vengono stampati solo build e test, il messaggio di errore va su stderr e lo stato è 1. Riesegui con 0 per tornare al percorso riuscito. ${1:-0} usa 0 quando il primo argomento manca o è vuoto: per questa prova usa soltanto 0 o 1.
Una funzione definita con nome() { ...; } assegna un nome a un gruppo di comandi; return ne fornisce lo stato. Salvare immediatamente lo stato del flusso in lesson_status impedisce al messaggio diagnostico di sostituire il fallimento originale. Nelle attività reali, inserisci operazioni già preparate e verificate e gestisci esplicitamente ogni errore.
Usa il punto e virgola per passi brevi indipendenti, && per operazioni subordinate al successo e if con funzioni per ramificazioni e recupero. lavoro; pulizia esegue la pulizia dopo un normale errore del lavoro, ma non la garantisce se la shell termina prima o riceve un segnale. Progetta questi casi con strumenti come trap. Anche set -e non gestisce automaticamente ogni errore: condizioni e liste AND/OR hanno eccezioni, perciò controlla esplicitamente gli stati importanti.
Gli alias si espandono durante la lettura del comando
alias crea un nome breve usato spesso in Bash interattivo. Inserisci questa definizione, poi esegui ll o type ll a un nuovo prompt. Bash legge e analizza i comandi prima di eseguirli: definire e usare un alias nello stesso comando composto può quindi non produrre l’espansione attesa. Controlla prima che ll non sia già un alias personale da conservare.
alias ll='ls -l'
type ll
ll
Un alias riguarda normalmente la shell corrente e, in Bash non interattivo, non viene espanso senza un’impostazione esplicita. Evita che uno script dipenda per errore da un alias interattivo. Elimina soltanto l’alias creato per la prova con unalias ll. Per operazioni riutilizzabili che ricevono argomenti, considera una funzione di shell.
Distinguere variabili di shell e ambiente
Una variabile di shell è disponibile nella shell corrente. Dopo export, entra anche nell’ambiente dei processi figli avviati successivamente. Usa un nome riservato alla prova invece di una comune variabile di sistema:
LESSON_MODE=practice
printf '%s\n' "$LESSON_MODE"
export LESSON_MODE
printenv LESSON_MODE
Entrambe le uscite sono practice. La prima deriva dall’espansione nella shell corrente; la seconda legge l’ambiente ricevuto dal processo esterno printenv. export non modifica retroattivamente un’altra shell già attiva o il processo padre.
Per fornire un valore a un solo comando, usa NOME=valore comando. Al termine esegui unset LESSON_MODE nella shell corrente. Non riutilizzare come nomi di prova la variabile della home o il PATH del sistema.
Controllare l’ordine di esecuzione
- Se non sai come viene interpretato un nome, inizia con type -a nome.
- Se una condizione sorprende, esamina separatamente lo stato di ogni passo.
- Se un alias non funziona, verifica che Bash sia interattivo e che l’uso segua la definizione su una nuova riga.
- Se un processo figlio non vede una variabile, verifica che sia stata esportata.
Operatori di sequenza, alias e variabili influiscono tutti sull’esecuzione, ma non sono intercambiabili. Prima di collegare comandi che cambiano file, verifica il flusso con comandi a basso impatto come true, false e printf.
Documentazione ufficiale
Il manuale ufficiale di Bash distribuito da Debian documenta liste AND/OR, precedenza, gruppi e pipeline nelle sezioni SHELL GRAMMAR e Pipelines. Le sezioni ALIASES, COMMAND EXECUTION ed ENVIRONMENT spiegano rispettivamente espansione degli alias, risoluzione dei comandi e passaggio delle variabili.
Nello stesso manuale, consulta wait, export e return sotto SHELL BUILTIN COMMANDS e le regole di EXIT STATUS per verificare gli stati restituiti. Le limitazioni di errexit sono descritte nell’opzione -e del comando incorporato set.




