Grundsätzlich handelt es sich um eine Frage der Portabilität (und Zuverlässigkeit).
Anfangs echo
habe ich keine Option akzeptiert und nichts erweitert. Alles, was es tat, war die Ausgabe seiner Argumente, die durch ein Leerzeichen getrennt und durch ein Zeilenvorschubzeichen abgeschlossen wurden.
Nun dachte jemand, es wäre schön, wenn wir Dinge tun könnten, um echo "\n\t"
Zeilenvorschub- oder Tabulatorzeichen auszugeben oder die Option zu haben, das abschließende Zeilenvorschubzeichen nicht auszugeben.
Sie dachten dann viel genauer nach, fügten diese Funktionalität jedoch nicht der Shell hinzu (beispielsweise, perl
wenn in doppelten Anführungszeichen \t
ein Tabulatorzeichen steht) echo
.
David Korn realisiert den Fehler und stellte eine neue Form der Schale Zitate: $'...'
die durch später kopiert wurde bash
und zsh
aber es war viel zu spät von dieser Zeit.
Wenn nun ein Standard-UNIX echo
ein Argument empfängt, das die beiden Zeichen enthält, \
und t
statt sie auszugeben, gibt es ein Tabulatorzeichen aus. Und sobald \c
ein Argument angezeigt wird, wird die Ausgabe gestoppt (sodass auch die nachgestellte Zeile nicht ausgegeben wird).
Andere Shells / Unix-Anbieter / -Versionen haben sich für eine andere Vorgehensweise entschieden: Sie haben eine -e
Option zum Erweitern von Escape-Sequenzen und eine -n
Option hinzugefügt , mit der die nachgestellte Zeile nicht ausgegeben wird. Einige müssen -E
Escape-Sequenzen deaktivieren, andere -n
jedoch nicht -e
. Die Liste der von einer echo
Implementierung unterstützten Escape-Sequenzen muss nicht mit der Liste der von einer anderen Implementierung unterstützten identisch sein.
Sven Mascheck hat eine schöne Seite, die das Ausmaß des Problems zeigt .
Bei echo
Implementierungen, die Optionen unterstützen, gibt es im Allgemeinen keine Unterstützung für a --
, um das Ende von Optionen zu markieren ( echo
dies ist bei einigen nicht Bourne-ähnlichen Shells der Fall, und zsh unterstützt dies -
jedoch). Daher ist es beispielsweise schwierig, "-n"
mit echo
in auszugeben viele Muscheln.
Bei einigen Shells wie bash
¹ oder ksh93
² oder yash
( $ECHO_STYLE
variabel) hängt das Verhalten sogar davon ab, wie die Shell kompiliert wurde oder von der Umgebung (das Verhalten von GNU echo
ändert sich auch, wenn $POSIXLY_CORRECT
es sich in der Umgebung befindet und mit der Version 4 , zsh
mit der bsd_echo
Option, einige pdksh-basierte mit ihrer posix
Option oder ob sie als genannt werden sh
oder nicht). Es ist also nicht garantiert, dass sich zwei bash
echo
s, auch von derselben Version von bash
, gleich verhalten.
POSIX sagt: Wenn das erste Argument -n
Backslashes enthält, ist das Verhalten nicht spezifiziert . bash
Echo in dieser Hinsicht ist nicht POSIX, dh es echo -e
wird nicht so ausgegeben, -e<newline>
wie es POSIX erfordert. Die UNIX-Spezifikation ist strenger, sie verbietet -n
und erfordert die Erweiterung einiger Escape-Sequenzen, einschließlich \c
derjenigen, um die Ausgabe zu stoppen.
Diese Spezifikationen können hier nicht wirklich Abhilfe schaffen, da viele Implementierungen nicht konform sind. Sogar einige zertifizierte Systeme wie MacOS 5 sind nicht kompatibel.
Um die aktuelle Realität wirklich darzustellen, sollte POSIX tatsächlich sagen : Wenn das erste Argument mit dem ^-([eEn]*|-help|-version)$
erweiterten regulären Ausdruck übereinstimmt oder ein Argument umgekehrte Schrägstriche enthält (oder Zeichen, deren Codierung die Codierung des umgekehrten Schrägstrichs enthält, wie α
in Gebietsschemas unter Verwendung des BIG5-Zeichensatzes), ist das Verhalten wie folgt nicht spezifiziert.
Alles in allem wissen Sie nicht, was echo "$var"
ausgegeben wird, es sei denn, Sie können sicherstellen, dass $var
die Ausgabe keine Backslash-Zeichen enthält und nicht mit beginnt -
. Die POSIX-Spezifikation sagt uns tatsächlich, dass wir printf
in diesem Fall stattdessen verwenden sollen.
Das bedeutet also, dass Sie keine echo
unkontrollierten Daten anzeigen können. Mit anderen Worten, wenn Sie ein Skript schreiben und externe Eingaben (vom Benutzer als Argumente oder Dateinamen aus dem Dateisystem ...) verwendet werden, können Sie es nicht echo
zum Anzeigen verwenden.
Das ist in Ordnung:
echo >&2 Invalid file.
Das ist nicht:
echo >&2 "Invalid file: $file"
(Obwohl es bei einigen (nicht UNIX-kompatiblen) echo
Implementierungen wie bash
's einwandfrei funktioniert , wenn die xpg_echo
Option nicht auf die eine oder andere Weise aktiviert wurde, wie zum Zeitpunkt der Kompilierung oder über die Umgebung).
file=$(echo "$var" | tr ' ' _)
nicht OK in den meisten Implementierungen ist (Ausnahmen sind yash
mit ECHO_STYLE=raw
(mit dem Vorbehalt , dass yash
‚s Variablen nicht beliebige Sequenzen von Bytes so nicht willkürlich Dateinamen) und halten kann zsh
s‘ echo -E - "$var"
6 ).
printf
Andererseits ist es zuverlässiger, zumindest wenn es auf die grundlegende Verwendung von beschränkt ist echo
.
printf '%s\n' "$var"
$var
Gibt den Inhalt von gefolgt von einem Newline-Zeichen aus, unabhängig davon, welches Zeichen es enthalten darf.
printf '%s' "$var"
Gibt es ohne das nachfolgende Zeilenumbruchzeichen aus.
Nun gibt es auch Unterschiede zwischen den printf
Implementierungen. Es gibt einen Kern von Funktionen, die von POSIX spezifiziert werden, aber es gibt auch viele Erweiterungen. Einige unterstützen beispielsweise a %q
, um die Argumente in Anführungszeichen zu setzen, aber die Vorgehensweise ist von Shell zu Shell unterschiedlich. Einige unterstützen \uxxxx
Unicode-Zeichen. Das Verhalten ist printf '%10s\n' "$var"
bei Multi-Byte-Gebietsschemas unterschiedlich. Für gibt es mindestens drei verschiedene Ergebnisseprintf %b '\123'
Aber am Ende haben Sie printf
keine Probleme , wenn Sie sich an die POSIX-Funktionen halten und nicht versuchen, etwas Besonderes daraus zu machen.
Denken Sie jedoch daran, dass das erste Argument das Format ist und daher keine variablen / unkontrollierten Daten enthalten sollte.
Eine zuverlässigere echo
kann implementiert werden mit printf
:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
Die Subshell (die in den meisten Shell-Implementierungen das Erzeugen eines zusätzlichen Prozesses impliziert) kann bei Verwendung local IFS
mit vielen Shells oder durch Schreiben wie folgt vermieden werden :
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Anmerkungen
1. Wie ist bash
das echo
Verhalten kann geändert werden.
Mit bash
zur Laufzeit gibt es zwei Dinge, die das Verhalten steuern echo
(neben enable -n echo
oder neu zu definieren echo
als eine Funktion oder Alias): die xpg_echo
bash
Option , und ob bash
im Posix - Modus. posix
Modus kann aktiviert werden, wenn bash
aufgerufen wird als sh
oder wenn POSIXLY_CORRECT
in der Umgebung oder mit der posix
Option:
Standardverhalten auf den meisten Systemen:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
Erweitert Sequenzen entsprechend den Anforderungen von UNIX:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Es ehrt noch -n
und -e
(und -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Mit xpg_echo
und POSIX-Modus:
$ 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
Dieses Mal bash
ist sowohl POSIX- als auch UNIX-konform. Beachten Sie, dass im POSIX-Modus bash
immer noch keine POSIX-Konformität besteht, da die Ausgabe -e
in folgenden Sprachen nicht erfolgt :
$ env SHELLOPTS=posix bash -c 'echo -e'
$
Die Standardwerte für xpg_echo und Posix können mit den bei der Kompilierung definiert werden --enable-xpg-echo-default
und --enable-strict-posix-default
Optionen zum configure
Skript. Dies ist in der Regel das, was neuere Versionen von OS / X tun, um sie zu erstellen /bin/sh
. Normalerweise würde dies jedoch keine Unix / Linux-Implementierung / -Distribution tun, die bei /bin/bash
klarem Verstand wäre . Eigentlich stimmt das nicht, das /bin/bash
Oracle mit Solaris 11 (in einem optionalen Paket) ausgeliefert wird --enable-xpg-echo-default
(das war in Solaris 10 nicht der Fall).
2. Wie ksh93
ist das echo
Verhalten verändert werden.
In ksh93
, ob echo
Escape - Sequenzen erweitert oder nicht und erkennt Optionen ist abhängig von dem Inhalt der $PATH
und / oder $_AST_FEATURES
Umgebungsvariablen.
Wenn $PATH
eine Komponente enthalten ist, die /5bin
oder /xpg
vor der /bin
oder /usr/bin
-Komponente enthält , verhält sie sich wie SysV / UNIX (erweitert Sequenzen, akzeptiert keine Optionen). Findet es /ucb
oder /bsd
zuerst oder enthält es $_AST_FEATURES
7UNIVERSE = ucb
, dann verhält es sich wie BSD 3 ( -e
um die Erweiterung zu ermöglichen, erkennt es -n
).
Die Standardeinstellung ist systemabhängig, BSD unter Debian (siehe die Ausgabe builtin getconf; getconf UNIVERSE
in neueren Versionen von 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 für Echo -e?
Der Hinweis auf BSD für den Umgang mit der -e
Option ist hier etwas irreführend. Die meisten dieser unterschiedlichen und inkompatiblen echo
Verhaltensweisen wurden bei AT & T eingeführt:
\n
, \0ooo
, \c
In Programmierwerkbank UNIX (basierend auf Unix V6) und den Rest ( \b
, \r
...) in Unix System III Ref .
-n
in Unix V7 (von Dennis Ritchie Ref )
-e
in Unix V8 (von Dennis Ritchie Ref )
-E
selbst möglicherweise ursprünglich von bash
(CWRU / CWRU.chlog in Version 1.13.5 erwähnt, dass Brian Fox es am 18.10.1992 hinzufügte, GNU echo
kopierte es kurz darauf in sh-utils-1.8, das 10 Tage später veröffentlicht wurde)
Während die echo
builtin des sh
oder BSDs unterstützt hat , -e
seit dem Tag , sie es in den frühen 90er Jahren mit dem Almquist Shell gestartet, die Standalone echo
nicht Dienstprogramm bis heute nicht dort unterstützen ( FreeBSDecho
noch nicht unterstützt -e
, obwohl es tut Unterstützung -n
wie Unix V7 (und auch \c
nur am Ende des letzten Arguments).
Die Handhabung -e
wurde hinzugefügt ksh93
ist , echo
wenn in dem BSD - Universum in der ksh93r Version im Jahr 2006 veröffentlicht und kann bei der Kompilierung deaktiviert werden.
4. Verhaltensänderung des GNU-Echos in 8.31
Da coreutils 8,31 (und diese begehen ), GNU echo
erweitert nun Sequenzen durch Standard entkommen , wenn POSIXLY_CORRECT in der Umwelt ist, das Verhalten anzupassen bash -o posix -O xpg_echo
‚s echo
builtin (siehe Bug - Report ).
5. macOS echo
Die meisten Versionen von macOS haben eine UNIX-Zertifizierung von der OpenGroup erhalten .
Ihr sh
eingebautes System echo
ist kompatibel, da es bash
(eine sehr alte Version) xpg_echo
standardmäßig aktiviert ist, ihr eigenständiges echo
Dienstprogramm jedoch nicht. env echo -n
gibt nichts statt aus -n<newline>
, env echo '\n'
gibt \n<newline>
statt aus <newline><newline>
.
Das /bin/echo
ist der von FreeBSD , den Neuen - Zeile - Ausgang unterdrückt , wenn das erste Argumente -n
oder (seit 1995) , wenn das letzte Argument endet \c
, aber unterstützt keine andere Backslash Sequenzen von UNIX erforderlich, auch nicht \\
.
6. echo
Implementierungen, die beliebige Daten wörtlich ausgeben können
Genau genommen könnte man auch zählen, dass FreeBSD / macOS /bin/echo
über (nicht die echo
eingebaute Shell ) wo zsh
's echo -E - "$var"
oder yash
' s ECHO_STYLE=raw echo "$var"
( printf '%s\n' "$var"
) geschrieben werden könnte:
/bin/echo "$var
\c"
Implementierungen, die Folgendes unterstützen -E
und -n
für die Folgendes konfiguriert werden kann:
echo -nE "$var
"
Und zsh
's echo -nE - "$var"
( printf %s "$var"
) könnte geschrieben werden
/bin/echo "$var\c"
7. _AST_FEATURES
und der ASTUNIVERSE
Das _AST_FEATURES
ist nicht dazu gedacht, direkt manipuliert zu werden. Es wird verwendet, um AST-Konfigurationseinstellungen über die Befehlsausführung zu verbreiten. Die Konfiguration soll über die (undokumentierte) astgetconf()
API erfolgen. Im Inneren ksh93
ist das getconf
eingebaute (mit builtin getconf
oder durch Aufrufen aktiviert command /opt/ast/bin/getconf
) die Schnittstelle zuastgetconf()
Beispielsweise müssen Sie builtin getconf; getconf UNIVERSE = att
die UNIVERSE
Einstellung auf ändern att
( echo
unter anderem , um das SysV-Verhalten zu beeinflussen). Danach werden Sie feststellen, dass die $_AST_FEATURES
Umgebungsvariable enthält UNIVERSE = att
.
echo -e
?