Salta al contenuto principale

echo: stampa testo e variabili

Inviato da tuxsa il

echo scrive a schermo il testo che gli passi, seguito da un a capo. È il comando più semplice del terminale e uno dei più usati: serve a mostrare messaggi, a leggere il valore di una variabile, a scrivere una riga in un file e a fare prove prima di eseguire comandi pericolosi.

Cos'è

echo significa "eco": ripete ciò che riceve. Esiste in due versioni: come comando interno (builtin) della shell, che è quella usata normalmente, e come eseguibile separato, /usr/bin/echo, del pacchetto GNU coreutils. Come visto per pwd, puoi verificarlo con:

$ type -a echo
echo is a shell builtin
echo is /usr/bin/echo

Le due versioni si comportano in modo quasi identico, ma non del tutto: le differenze di dettaglio sono la ragione per cui negli script più delicati si preferisce printf (ne parliamo in fondo).

Cosa fa

Prende i suoi argomenti, li separa con uno spazio e li stampa sullo standard output, aggiungendo un carattere di a capo finale. Non legge file e non modifica nulla, a meno che tu non reindirizzi il suo output. Una cosa da tenere sempre a mente: il lavoro di interpretare variabili, caratteri jolly e virgolette lo fa la shell prima ancora che echo parta. A echo arriva già il testo risolto.

Come si usa

$ echo [opzioni] [testo...]

Esempio 1: stampare un messaggio

$ echo Ciao, mondo
Ciao, mondo
$ echo "Ciao, mondo"
Ciao, mondo

Le virgolette non fanno parte del testo: servono alla shell. Per i messaggi con punteggiatura o spazi conviene sempre metterle.

Esempio 2: le virgolette contano

$ echo uno    due     tre
uno due tre
$ echo "uno    due     tre"
uno    due     tre

Senza virgolette la shell spezza il testo in parole e butta via gli spazi in eccesso: echo riceve tre argomenti e li rimette insieme con uno spazio solo. Con le virgolette riceve un unico argomento e lo stampa com'è.

Esempio 3: leggere una variabile

$ echo "$HOME"
/home/enzo
$ echo "Utente: $USER, shell: $SHELL"
Utente: enzo, shell: /bin/bash
$ echo "$PATH"
/usr/local/bin:/usr/bin:/bin

Il segno $ davanti a un nome dice alla shell di sostituirlo con il valore della variabile. echo è il modo più rapido per controllare cosa contiene una variabile d'ambiente.

Esempio 4: apici singoli e doppi

$ echo "Casa: $HOME"
Casa: /home/enzo
$ echo 'Casa: $HOME'
Casa: $HOME

Le virgolette doppie lasciano agire la shell sulle variabili; gli apici singoli la bloccano del tutto e il testo resta letterale. Se nel testo c'è un apostrofo, usa le virgolette doppie: echo "L'altra casa".

Esempio 5: inserire il risultato di un comando

$ echo "Oggi è $(date +%A)"
Oggi è mercoledì
$ echo "Sono in: $(pwd)"
Sono in: /home/enzo
$ echo "File in questa cartella: $(ls | wc -l)"
File in questa cartella: 12

La forma $(comando) viene sostituita dall'output del comando.

Esempio 6: il codice di uscita con $?

$ ls /cartella_inesistente
ls: impossibile accedere a '/cartella_inesistente': File o directory non esistente
$ echo $?
2
$ ls /etc > /dev/null
$ echo $?
0

La variabile speciale $? contiene il codice di uscita dell'ultimo comando: 0 significa successo, qualsiasi altro numero un errore. È uno degli usi più frequenti di echo quando si prova uno script. Altre variabili utili da guardare con echo: $$ (il PID della shell), $0 (il nome della shell o dello script), $# (quanti argomenti ha ricevuto uno script).

Esempio 7: senza a capo con -n

$ echo -n "Continuo..."
Continuo...$
$ echo "ciao" | wc -c
5
$ echo -n "ciao" | wc -c
4

Con -n echo non aggiunge l'a capo finale (nel primo esempio il prompt rimane attaccato al testo). La differenza conta quando si conta o si elabora il testo, come mostra wc -c: "ciao" ha 4 caratteri, ma echo normalmente ne aggiunge uno, l'a capo.

Esempio 8: sequenze speciali con -e

$ echo -e "riga 1\nriga 2"
riga 1
riga 2
$ echo -e "nome\tcognome"
nome	cognome

Con -e echo interpreta le sequenze introdotte dalla barra rovesciata. Le principali:

SequenzaSignificato
\nA capo
\tTabulazione
\\Una barra rovesciata
\aSegnale acustico (il "beep" del terminale)
\e o \033Carattere di escape, per i colori del terminale

Senza -e il testo resta com'è: echo "a\nb" stampa proprio a\nb. In Bash esiste anche un'alternativa, la citazione $'...', che interpreta le sequenze senza bisogno di -e:

$ echo $'riga 1\nriga 2'

Esempio 9: messaggi colorati

$ echo -e "\e[31mErrore\e[0m: file non trovato"
$ echo -e "\e[32mFatto\e[0m"

Le sequenze \e[...m cambiano il colore del testo nel terminale: 31 rosso, 32 verde, 33 giallo, 34 blu, 0 ripristina i colori normali. Dimenticare il \e[0m finale lascia il terminale colorato fino al comando successivo.

Esempio 10: scrivere in un file

$ echo "prima riga" > note.txt
$ echo "seconda riga" >> note.txt
$ cat note.txt
prima riga
seconda riga

Con > l'output di echo finisce in un file, che viene creato o svuotato; con >> viene aggiunto in coda. È il modo più veloce per creare un file con una riga di contenuto, a differenza di touch, che lo crea vuoto.

Esempio 11: vedere cosa farebbe la shell

$ ls
appunti.txt  cache.tmp  lista.txt  vecchio.tmp
$ echo *
appunti.txt cache.tmp lista.txt vecchio.tmp
$ echo *.tmp
cache.tmp vecchio.tmp
$ echo rm *.tmp
rm cache.tmp vecchio.tmp

I caratteri jolly li espande la shell, non echo: scrivere echo *.tmp mostra l'elenco esatto dei file che il carattere jolly selezionerebbe. L'ultimo comando, con echo davanti, è un'anteprima: mostra il comando che verrebbe eseguito, senza eseguirlo. È un'ottima abitudine prima di un rm con caratteri jolly.

Esempio 12: espansioni con le parentesi graffe

$ echo {1..5}
1 2 3 4 5
$ echo file{1..3}.txt
file1.txt file2.txt file3.txt
$ echo {a,b,c}
a b c
$ echo ~
/home/enzo

Anche queste espansioni sono compito della shell, e echo è un modo comodo per provarle prima di usarle con mkdir o touch.

Esempio 13: in coda ad altri comandi

$ echo "3 1 2" | tr ' ' '\n' | sort
1
2
3
$ echo "ciao mondo" | tr a-z A-Z
CIAO MONDO
$ echo "testo di prova" | sed 's/prova/esempio/'
testo di esempio

Con la pipe | si dà a un altro comando un testo da elaborare, senza creare un file. Il comando tr sostituisce o elimina caratteri; sed lo conosci già.

Esempio 14: messaggi di errore e di debug

$ echo "Errore: file non trovato" >&2

Con >&2 il messaggio va sul canale degli errori (stderr) invece che sull'output normale. Negli script è la convenzione per i messaggi di errore e di debug, perché non si mescolano con i risultati che qualcuno potrebbe reindirizzare o passare a un altro comando.

Esempio 15: uso negli script

#!/bin/bash
NOME="${1:-mondo}"

echo "Ciao, $NOME!"
echo "Oggi è $(date +%A)"
echo "Sono in: $(pwd)"
echo "Argomenti ricevuti: $#"

if [ ! -d "$HOME/backup" ]; then
    echo "Cartella backup assente" >&2
    exit 1
fi

VERDE='\e[32m'
RESET='\e[0m'
echo -e "${VERDE}Tutto a posto${RESET}"

La costruzione ${1:-mondo} prende il primo argomento dello script e, se manca, usa "mondo". Lanciato come ./saluto.sh Enzo, stampa "Ciao, Enzo!".

Esempio 16: la versione portabile, printf

$ printf "%s\n" "Ciao, mondo"
Ciao, mondo
$ printf "%s ha %d anni\n" Mario 42
Mario ha 42 anni
$ printf "%-10s|%5d|\n" mele 12
mele      |   12|

Il comando printf funziona come in linguaggio C: si scrive un modello con dei segnaposto (%s per il testo, %d per i numeri interi) e poi i valori. Non aggiunge l'a capo se non lo scrivi (\n) e si comporta in modo uguale in tutte le shell. Con %-10s il testo viene allineato a sinistra su 10 caratteri, con %5d il numero a destra su 5: utile per le tabelle.

Opzioni utili

  • -n: non aggiunge l'a capo finale.
  • -e: interpreta le sequenze con la barra rovesciata (\n, \t, \e...).
  • -E: non le interpreta (è il comportamento predefinito in Bash).
  • --help e --version: sono riconosciute solo dall'eseguibile /usr/bin/echo, non dal comando interno di Bash (per quello c'è help echo).

Attenzione a

  • Le variabili senza virgolette: echo $VAR è rischioso se il valore contiene spazi o caratteri jolly. Esempio: con VAR="*", echo $VAR stampa l'elenco dei file della cartella corrente, e non l'asterisco. Scrivi sempre echo "$VAR".
  • Il punto esclamativo: in una shell interattiva, echo "Ciao!" può dare bash: !": event not found, perché Bash interpreta ! come richiamo della cronologia, anche tra virgolette doppie. Usa gli apici singoli (echo 'Ciao!') oppure disattiva la funzione con set +H. Negli script il problema non c'è.
  • > sovrascrive: echo "x" > file svuota il file prima di scriverci. Per aggiungere usa >>.
  • Con sudo: sudo echo "riga" >> /etc/hosts non funziona, perché il reindirizzamento lo esegue la tua shell senza privilegi. Usa echo "riga" | sudo tee -a /etc/hosts, come visto nell'articolo su sudo.
  • L'a capo finale cambia il risultato: nei calcoli di somme di controllo, nel conteggio dei caratteri o quando si passa un valore a un altro programma, echo "abc" invia abc più l'a capo. Un esempio classico:

    $ echo "abc" | sha256sum
    edeaaff3f1774ad2...  -
    $ echo -n "abc" | sha256sum
    ba7816bf8f01cfea...  -

    Sono due somme di controllo diverse, perché il testo è diverso. Se ti serve il testo esatto, usa echo -n o printf.

  • Portabilità di -e e -n: non tutte le shell si comportano come Bash. Nella shell sh di Debian e Ubuntu (dash) l'echo interno interpreta già le sequenze con la barra rovesciata e non riconosce -e, che stamperebbe come testo. Se lo script ha come prima riga #!/bin/sh, usa printf; con #!/bin/bash puoi usare echo -e.
  • Testo che comincia con un trattino: se la variabile contiene qualcosa come -n o -e, echo "$VAR" lo prende per un'opzione e non lo stampa. Per stampare in modo sicuro un valore qualsiasi usa printf '%s\n' "$VAR".
  • Mai le password sulla riga di comando: echo "miapassword" | programma lascia la password nella cronologia dei comandi (history). Passarla come argomento di un programma la rende visibile anche a chiunque guardi l'elenco dei processi con ps. Per le password, i programmi hanno di solito un'opzione per leggerle da file o da un input riservato.
  • echo riesce quasi sempre: il suo codice di uscita è 0 anche se stampa un messaggio di errore. Non usarlo per capire se un comando è andato a buon fine: guarda $? o il test con if.
  • Sequenze di colore: i codici \e[...m dipendono dal terminale. Se l'output viene reindirizzato in un file, i codici vengono scritti nel file come caratteri strani (li vedi con cat -A). Negli script conviene colorare solo se l'output va a un terminale.

Riepilogo

ComandoCosa fa
echo "testo"Stampa il testo con un a capo
echo "$VAR"Mostra il valore di una variabile
echo 'testo $VAR'Stampa il testo letterale, senza espansioni
echo "Oggi: $(date +%F)"Inserisce l'output di un comando
echo $?Codice di uscita dell'ultimo comando
echo -n "testo"Stampa senza a capo finale
echo -e "a\nb"Interpreta le sequenze con la barra rovesciata
echo "x" > file / >> fileScrive nel file / aggiunge in coda
echo *.tmpMostra cosa selezionerebbe un carattere jolly
echo rm *.tmpAnteprima di un comando, senza eseguirlo
echo "errore" >&2Scrive sul canale degli errori
printf '%s\n' "$VAR"Alternativa portabile e sicura