Fondamentalmente, è un problema di portabilità (e affidabilità).
Inizialmente, echonon accettava alcuna opzione e non espandeva nulla. Tutto ciò che stava facendo era presentare i suoi argomenti separati da un carattere spaziale e terminati da un carattere di nuova riga.
Ora, qualcuno ha pensato che sarebbe bello se potessimo fare cose come echo "\n\t"l'output di caratteri di nuova riga o di tabulazione, o avere un'opzione per non generare il carattere di nuova riga finale.
Hanno poi pensato più intensamente ma invece di aggiungere quella funzionalità alla shell (come perldove all'interno delle doppie virgolette, in \trealtà significa un carattere di tabulazione), l'hanno aggiunta a echo.
David Korn si rese conto dell'errore e introdusse una nuova forma di citazioni di shell: $'...'che fu successivamente copiata bashe zshma era ormai troppo tardi.
Ora quando un UNIX standard echoriceve un argomento che contiene i due caratteri \e t, invece di emetterli, genera un carattere di tabulazione. E non appena vede \cin un argomento, smette di essere emesso (quindi non viene emessa neanche la nuova riga finale).
Altre shell / fornitori / versioni di Unix hanno scelto di farlo diversamente: hanno aggiunto -eun'opzione per espandere le sequenze di escape e -nun'opzione per non generare la nuova riga finale. Alcuni hanno l'obbligo -Edi disabilitare le sequenze di escape, altri invece hanno, -nma -el'elenco delle sequenze di escape supportate da echoun'implementazione non è necessariamente lo stesso di quello supportato da un'altra.
Sven Mascheck ha una bella pagina che mostra l'entità del problema .
Per questi echoimplementazioni che supportano le opzioni, non c'è generalmente alcun supporto di una --per segnare la fine delle opzioni (il echobuiltin di alcune conchiglie di non-Bourne-come fare, e zsh supporti -per questo però), così per esempio, è difficile uscita "-n"con echoin molte conchiglie.
Su alcune shell come bash¹ o ksh93² o yash( $ECHO_STYLEvariabile), il comportamento dipende anche da come è stata compilata la shell o dall'ambiente ( echoil comportamento di GNU cambierà anche se si $POSIXLY_CORRECTtrova nell'ambiente e con la versione 4 , zshcon la sua bsd_echoopzione, alcuni basati su pdksh con la loro posixopzione o se sono chiamati come sho meno). Quindi non è garantito che due bash echosecondi, anche della stessa versione, bashsi comportino allo stesso modo.
POSIX dice: se il primo argomento è -no qualsiasi argomento contiene barre rovesciate, il comportamento non è specificato . bashl'eco al riguardo non è POSIX in quanto, ad esempio, echo -enon viene emessa -e<newline>come POSIX richiede. La specifica UNIX è più rigorosa, proibisce -ne richiede l'espansione di alcune sequenze di escape tra cui \cquella per interrompere l'output.
Queste specifiche non vengono davvero in soccorso qui dato che molte implementazioni non sono conformi. Anche alcuni sistemi certificati come macOS 5 non sono conformi.
Per rappresentare realmente la realtà attuale, POSIX dovrebbe effettivamente dire : se il primo argomento corrisponde alla ^-([eEn]*|-help|-version)$regexp estesa o qualsiasi argomento contiene barre rovesciate (o caratteri la cui codifica contiene la codifica del carattere barra rovesciata come αnelle localizzazioni 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 $varnon contenga caratteri di barra rovesciata e non inizi con -. La specifica POSIX in realtà ci dice di usare printfinvece in quel caso.
Ciò significa che non è possibile utilizzare echoper visualizzare dati non controllati. In altre parole, se stai scrivendo uno script e sta prendendo input esterno (dall'utente come argomenti o nomi di file dal file system ...), non puoi usarlo echoper visualizzarlo.
Questo va bene:
echo >&2 Invalid file.
Questo non è:
echo >&2 "Invalid file: $file"
(Anche se funzionerà bene con alcune echoimplementazioni (non conformi a UNIX) come quelle bashquando l' xpg_echoopzione non è stata abilitata in un modo o nell'altro come al momento della compilazione o tramite l'ambiente).
file=$(echo "$var" | tr ' ' _)non è OK nella maggior parte delle implementazioni (ad eccezione yashdi ECHO_STYLE=raw(con l'avvertenza che yashle variabili non possono contenere sequenze arbitrarie di byte, quindi non nomi di file arbitrari) e zsh' echo -E - "$var"6 ).
printf, d'altra parte è più affidabile, almeno quando è limitato all'utilizzo di base di echo.
printf '%s\n' "$var"
Produrrà il contenuto $varseguito da un carattere di nuova riga indipendentemente dal personaggio che può contenere.
printf '%s' "$var"
Lo produrrà senza il carattere di nuova riga finale.
Ora, ci sono anche differenze tra le printfimplementazioni. C'è un nucleo di funzionalità che è specificato da POSIX, ma poi ci sono molte estensioni. Ad esempio, alcuni supportano a %qper citare gli argomenti, ma il modo in cui viene fatto varia da shell a shell, alcuni supportano i \uxxxxcaratteri Unicode. Il comportamento varia printf '%10s\n' "$var"in locali multi-byte, ci sono almeno tre risultati diversi perprintf %b '\123'
Ma alla fine, se ti attieni al set di funzionalità POSIX printfe non provi a fare qualcosa di troppo fantasioso, sei senza problemi.
Ma ricorda che il primo argomento è il formato, quindi non dovrebbe contenere dati variabili / non controllati.
Un più affidabile echopuò essere implementato usando printf, come:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
La subshell (che implica la generazione di un ulteriore processo nella maggior parte delle implementazioni della shell) può essere evitata usando local IFScon molte shell o scrivendola come:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Appunti
1. come bashil echocomportamento può essere modificato.
Con bash, in fase di esecuzione, ci sono due cose che controllano il comportamento di echo(accanto enable -n echoo ridefinendo echocome funzione o alias): l' xpg_echo bashopzione e se bashè in modalità posix. posixla modalità può essere abilitata se bashviene chiamata come sho se si POSIXLY_CORRECTtrova nell'ambiente o con l' posixopzione:
Comportamento predefinito sulla maggior parte dei sistemi:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo espande le sequenze quando UNIX richiede:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Onora ancora -ne -e(e -E):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Con xpg_echoe modalità POSIX:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A
Questa volta bashè conforme sia a POSIX che a UNIX. Si noti che in modalità POSIX, non bashè ancora conforme a POSIX in quanto non viene emesso -ein:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
I valori predefiniti per xpg_echo e posix possono essere definiti al momento della compilazione con le opzioni --enable-xpg-echo-defaulte --enable-strict-posix-defaultper lo configurescript. Questo è in genere ciò che fanno le versioni recenti di OS / X per crearne una /bin/sh. Tuttavia, nessuna implementazione / distribuzione Unix / Linux nella loro mente corretta in genere lo farebbe/bin/bash . In realtà, questo non è vero, quello con /bin/bashcui Oracle viene fornito con Solaris 11 (in un pacchetto opzionale) sembra essere costruito con --enable-xpg-echo-default(non era il caso in Solaris 10).
2. Come ksh93s' echocomportamento può essere modificato.
In ksh93, se echoespande le sequenze di escape o meno e riconosce le opzioni dipende dal contenuto delle variabili di ambiente $PATHe / o $_AST_FEATURES.
Se $PATHcontiene un componente che contiene /5bino /xpgprima del componente /bino /usr/bin, si comporta in modo SysV / UNIX (espande le sequenze, non accetta le opzioni). Se trova /ucbo /bsdprima o se $_AST_FEATURES7 contiene UNIVERSE = ucb, si comporta in modo BSD 3 ( -eper consentire l'espansione, riconosce -n).
L'impostazione predefinita dipende dal sistema, BSD su Debian (vedere l'output di builtin getconf; getconf UNIVERSEnelle ultime versioni di ksh93):
$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD
3. BSD per l'eco -e?
Il riferimento a BSD per la gestione -edell'opzione è un po 'fuorviante qui. La maggior parte di questi echocomportamenti diversi e incompatibili sono stati tutti introdotti in AT&T:
\n, \0ooo, \cIn panchina per il programmatore di lavoro UNIX (basato su Unix V6), e il resto ( \b, \r...) in UNIX System III Ref .
-nin Unix V7 (di Dennis Ritchie Ref )
-ein Unix V8 (di Dennis Ritchie Ref )
-Estesso probabilmente originariamente proveniva da bash(CWRU / CWRU.chlog nella versione 1.13.5 menziona Brian Fox aggiungendolo nel 1992-10-18, GNU echocopiandolo poco dopo in sh-utils-1.8 rilasciato 10 giorni dopo)
Mentre il echobuiltin del sho BSD ha supportato -edal giorno in cui hanno iniziato a utilizzare la shell Almquist per esso nei primi anni '90, l' echoutilità autonoma fino ad oggi non lo supporta lì ( FreeBSDecho non supporta ancora -e, sebbene supporti -ncome Unix V7 (e anche \cma solo alla fine dell'ultimo argomento)).
La gestione di -estato aggiunto al ksh93's echoquando nella BSD nell'universo nella versione ksh93r uscito nel 2006 e può essere disabilitato in fase di compilazione.
4. GNU fa eco al cambiamento di comportamento in 8.31
Dal momento che coreutils 8.31 (e questo commettono ), GNU echoora si espande sequenze di escape per impostazione predefinita quando POSIXLY_CORRECT è nell'ambiente, per abbinare il comportamento di bash -o posix -O xpg_echo's echoincorporato (vedi bug report ).
5. macOS echo
La maggior parte delle versioni di macOS ha ricevuto la certificazione UNIX da OpenGroup .
Il loro shbuilt-in echoè conforme in quanto è bash(una versione molto vecchia) costruito con xpg_echoabilitato per impostazione predefinita, ma la loro echoutilità autonoma non lo è. env echo -nnon genera nulla invece di -n<newline>, env echo '\n'genera \n<newline>invece di <newline><newline>.
Questo /bin/echoè quello di FreeBSD che sopprime l'output newline se il primo argomento è -no (dal 1995) se l'ultimo argomento finisce \c, ma non supporta altre sequenze di barra rovesciata richieste da UNIX, nemmeno \\.
6. echoimplementazioni in grado di produrre dati arbitrari alla lettera
A rigor di termini, puoi anche contare che FreeBSD / macOS /bin/echosopra (non il echobuiltin della loro shell ) dove si potrebbe scrivere zsh' echo -E - "$var"o yash' ECHO_STYLE=raw echo "$var"( printf '%s\n' "$var")
/bin/echo "$var
\c"
Le implementazioni che supportano -Ee -n(o possono essere configurate per) possono anche fare:
echo -nE "$var
"
E zsh' echo -nE - "$var"( printf %s "$var") potrebbe essere scritto
/bin/echo "$var\c"
7. _AST_FEATURESe ASTUNIVERSE
Non _AST_FEATURESè destinato a essere manipolato direttamente, ma viene utilizzato per propagare le impostazioni di configurazione AST attraverso l'esecuzione del comando. La configurazione deve essere eseguita tramite l' astgetconf()API (non documentata) . All'interno ksh93, il getconfbuiltin (abilitato con builtin getconfo tramite invocazione command /opt/ast/bin/getconf) è l'interfaccia aastgetconf()
Ad esempio, dovresti builtin getconf; getconf UNIVERSE = attmodificare l' UNIVERSEimpostazione su att(causando il echocomportamento SysV tra le altre cose). Dopo averlo fatto, noterai che la $_AST_FEATURESvariabile di ambiente contiene UNIVERSE = att.
echo -e?