Ho sentito che printf
è meglio di echo
. Posso ricordare solo un'istanza dalla mia esperienza in cui ho dovuto usare printf
perché echo
non ha funzionato per inserire del testo in qualche programma su RHEL 5.8 ma printf
fatto. Ma a quanto pare ci sono altre differenze, e vorrei chiedere quali sono e se ci sono casi specifici in cui utilizzare l'una rispetto all'altra.
Risposta accettata:
Fondamentalmente, è un problema di portabilità (e affidabilità).
Inizialmente, echo
non ha accettato alcuna opzione e non ha ampliato nulla. Tutto quello che stava facendo era emettere i suoi argomenti separati da uno spazio e terminati da un carattere di nuova riga.
Qualcuno ha pensato che sarebbe bello se potessimo fare cose come echo "nt"
per generare caratteri di nuova riga o di tabulazione oppure avere un'opzione per non generare il carattere di nuova riga finale.
Hanno quindi riflettuto di più ma invece di aggiungere quella funzionalità alla shell (come perl
dove tra virgolette doppie, t
in realtà significa un carattere di tabulazione), lo hanno aggiunto a echo
.
David Korn si rese conto dell'errore e introdusse una nuova forma di virgolette di shell:$'...'
che è stato poi copiato da bash
e zsh
ma ormai era troppo tardi.
Ora quando uno standard UNIX echo
riceve un argomento che contiene i due caratteri e
t
, invece di emetterli, restituisce un carattere di tabulazione. E non appena vede c
in un argomento, interrompe l'output (quindi non viene nemmeno emesso il newline finale).
Altre shell/fornitori/versioni Unix hanno scelto di farlo in modo diverso:hanno aggiunto un -e
opzione per espandere le sequenze di escape e un -n
opzione per non produrre l'ultima riga finale. Alcuni hanno un -E
per disabilitare le sequenze di escape, alcuni hanno -n
ma non -e
, l'elenco delle sequenze di escape supportate da un echo
l'implementazione non è necessariamente la stessa supportata da un altro.
Sven Mascheck ha una bella pagina che mostra l'entità del problema.
Su quelle echo
implementazioni che supportano le opzioni, generalmente non c'è il supporto di un --
per contrassegnare la fine delle opzioni (il echo
builtin di alcune shell non Bourne lo fanno, e zsh supporta -
per questo però), quindi, ad esempio, è difficile produrre "-n"
con echo
in molte conchiglie.
Su alcune shell come bash
¹ o ksh93
² o yash
($ECHO_STYLE
variabile), il comportamento dipende anche da come è stata compilata la shell o dall'ambiente (GNU echo
Il comportamento di 's cambierà anche se $POSIXLY_CORRECT
è nell'ambiente e con la versione zsh
's con il suo bsd_echo
opzione, alcuni basati su pdksh con il loro posix
opzione o se sono chiamati come sh
o no). Quindi due bash
echo
s, anche dalla stessa versione di bash
non è garantito che si comportino allo stesso modo.
POSIX dice:se il primo argomento è -n
o qualsiasi argomento contiene barre inverse, il comportamento non è specificato . bash
echo a questo proposito non è POSIX in quanto ad esempio echo -e
non emette -e<newline>
come richiede POSIX. La specifica UNIX è più rigorosa, proibisce -n
e richiede l'espansione di alcune sequenze di escape incluso il c
uno per interrompere l'output.
Queste specifiche non vengono davvero in soccorso qui dato che molte implementazioni non sono conformi. Anche alcuni certificati sistemi come macOS non sono conformi.
Per rappresentare realmente la realtà attuale, POSIX dovrebbe effettivamente dire:se il primo argomento corrisponde a ^-([eEn]*|-help|-version)$
regexp esteso o qualsiasi argomento contiene barre rovesciate (o caratteri la cui codifica contiene la codifica del carattere barra rovesciata come α
in locali che utilizzano il set di caratteri BIG5), il comportamento non è specificato.
Tutto sommato, non sai cosa echo "$var"
verrà prodotto a meno che tu non possa assicurarti che $var
non contiene caratteri backslash e non inizia con -
. La specifica POSIX in realtà ci dice di usare printf
invece in quel caso.
Quindi ciò significa che non puoi usare echo
per visualizzare dati non controllati. In altre parole, se stai scrivendo uno script e sta ricevendo input esterni (dall'utente come argomenti, o nomi di file dal file system...), non puoi usare echo
per visualizzarlo.
Questo va bene:
echo >&2 Invalid file.
Questo non è:
echo >&2 "Invalid file: $file"
(Anche se funzionerà bene con alcuni (non conformi a UNIX) echo
implementazioni come bash
's quando xpg_echo
l'opzione non è stata abilitata in un modo o nell'altro come al momento della compilazione o tramite l'ambiente).
file=$(echo "$var" | tr ' ' _)
non va bene nella maggior parte delle implementazioni (eccezioni che sono yash
con ECHO_STYLE=raw
(con l'avvertenza che yash
Le variabili 's non possono contenere sequenze arbitrarie di byte, quindi non nomi di file arbitrari) e zsh
's echo -E - "$var"
).
printf
, d'altra parte è più affidabile, almeno quando è limitato all'utilizzo di base di echo
.
printf '%sn' "$var"
Verrà visualizzato il contenuto di $var
seguito da un carattere di nuova riga indipendentemente dal carattere che può contenere.
printf '%s' "$var"
Lo produrrà senza il carattere di nuova riga finale.
Ora, ci sono anche differenze tra printf
implementazioni. C'è un nucleo di funzionalità che è specificato da POSIX, ma poi ci sono molte estensioni. Ad esempio, alcuni supportano un %q
per citare gli argomenti ma il modo in cui è fatto varia da shell a shell, alcuni supportano uxxxx
per i caratteri Unicode. Il comportamento varia per printf '%10sn' "$var"
nelle impostazioni locali multibyte, ci sono almeno tre diversi risultati per printf %b '123'
Ma alla fine, se ti attieni al set di funzionalità POSIX di printf
e non provare a farci niente di troppo fantasioso, sei fuori dai guai.
Ma ricorda che il primo argomento è il formato, quindi non dovrebbe contenere dati variabili/non controllati.
Un echo
più affidabile può essere implementato usando printf
, come:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
La sottoshell (che implica la generazione di un processo aggiuntivo nella maggior parte delle implementazioni della shell) può essere evitata usando local IFS
con molte conchiglie, oppure scrivendolo come:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
if [ "$#" -gt 0 ]; then
printf ' %s' "[email protected]"
fi
fi
printf 'n'
}
Note
1. come bash
's echo
il comportamento può essere modificato.
Con bash
, in fase di esecuzione, ci sono due cose che controllano il comportamento di echo
(accanto a enable -n echo
o ridefinendo echo
come funzione o alias):
il xpg_echo
bash
opzione e se bash
è in modalità posix. posix
la modalità può essere abilitata se bash
si chiama sh
o se POSIXLY_CORRECT
è nell'ambiente o con il posix
opzione:
Comportamento predefinito sulla maggior parte dei sistemi:
$ bash -c 'echo -n "