Ich habe gehört, dass printf
ist besser als echo
. Ich kann mich aus meiner Erfahrung nur an einen Fall erinnern, in dem ich printf
verwenden musste weil echo
funktionierte nicht, um Text in ein Programm unter RHEL 5.8 einzuspeisen, aber printf
tat. Aber anscheinend gibt es noch andere Unterschiede, und ich würde gerne fragen, welche das sind und ob es bestimmte Fälle gibt, in denen das eine im Vergleich zum anderen verwendet wird.
Akzeptierte Antwort:
Im Grunde ist es ein Problem der Portabilität (und Zuverlässigkeit).
Anfänglich echo
akzeptierte keine Option und erweiterte nichts. Alles, was es tat, war die Ausgabe seiner Argumente, getrennt durch ein Leerzeichen und abgeschlossen durch ein Zeilenumbruchzeichen.
Nun, jemand dachte, es wäre schön, wenn wir Dinge wie echo "nt"
machen könnten Zeilenumbruch- oder Tabulatorzeichen auszugeben oder das abschließende Zeilenumbruchzeichen nicht auszugeben.
Sie dachten dann genauer nach, aber anstatt diese Funktionalität zur Shell hinzuzufügen (wie perl
wo in doppelten Anführungszeichen, t
bedeutet eigentlich ein Tabulatorzeichen), sie haben es zu echo
hinzugefügt .
David Korn erkannte den Fehler und führte eine neue Form von Shell-Anführungszeichen ein:$'...'
die später von bash
kopiert wurde und zsh
aber zu diesem Zeitpunkt war es schon viel zu spät.
Jetzt, wenn ein Standard-UNIX echo
erhält ein Argument, das die beiden Zeichen enthält und
t
, statt sie auszugeben, gibt es ein Tabulatorzeichen aus. Und sobald es c
sieht in einem Argument stoppt es die Ausgabe (also wird auch der abschließende Zeilenumbruch nicht ausgegeben).
Andere Shells/Unix-Anbieter/Versionen entschieden sich dafür, es anders zu machen:Sie fügten ein -e
hinzu Option zum Erweitern von Escape-Sequenzen und ein -n
Option, um den abschließenden Zeilenumbruch nicht auszugeben. Einige haben ein -E
Um Escape-Sequenzen zu deaktivieren, haben einige -n
aber nicht -e
, die Liste der Escape-Sequenzen, die von einem echo
unterstützt werden Die Implementierung ist nicht unbedingt die gleiche wie die von einer anderen unterstützte.
Sven Mascheck hat eine schöne Seite, die das Ausmaß des Problems zeigt.
Auf diesen echo
Implementierungen, die Optionen unterstützen, wird im Allgemeinen kein --
unterstützt um das Ende von Optionen zu markieren (das echo
Eingebaute Shells, die nicht Bourne-ähnlich sind, tun dies, und zsh unterstützt -
dafür), also ist es zum Beispiel schwierig, "-n"
auszugeben mit echo
in vielen Muscheln.
Auf einigen Shells wie bash
¹ oder ksh93
² oder yash
($ECHO_STYLE
Variable), hängt das Verhalten sogar davon ab, wie die Shell kompiliert wurde oder von der Umgebung (GNU echo
Das Verhalten ändert sich auch, wenn $POSIXLY_CORRECT
ist in der Umgebung und mit der Version zsh
mit seinem bsd_echo
Option, einige pdksh-basiert mit ihrem posix
Option oder ob sie als sh
bezeichnet werden oder nicht). Also zwei bash
echo
s, sogar von derselben Version von bash
verhalten sich nicht garantiert gleich.
POSIX sagt:wenn das erste Argument -n
ist oder irgendein Argument Backslashes enthält, dann ist das Verhalten nicht spezifiziert . bash
echo ist in dieser Hinsicht nicht POSIX, zum Beispiel echo -e
gibt -e<newline>
nicht aus wie POSIX erfordert. Die UNIX-Spezifikation ist strenger, sie verbietet -n
und erfordert die Erweiterung einiger Escape-Sequenzen einschließlich des c
eine, um die Ausgabe zu stoppen.
Diese Spezifikationen helfen hier nicht wirklich, da viele Implementierungen nicht konform sind. Einige sogar zertifiziert Systeme wie macOS sind nicht konform.
Um wirklich die aktuelle Realität darzustellen, sollte POSIX eigentlich sagen:if the first argument does the ^-([eEn]*|-help|-version)$
erweiterter regulärer Ausdruck oder irgendein Argument enthält Backslashes (oder Zeichen, deren Codierung die Codierung des Backslash-Zeichens enthält, wie α
in Gebietsschemas, die den BIG5-Zeichensatz verwenden), ist das Verhalten nicht spezifiziert.
Alles in allem wissen Sie nicht, was echo "$var"
ist wird ausgegeben, es sei denn, Sie können sicherstellen, dass $var
enthält keine Backslash-Zeichen und beginnt nicht mit -
. Die POSIX-Spezifikation fordert uns tatsächlich auf, printf
zu verwenden stattdessen in diesem Fall.
Das bedeutet also, dass Sie echo
nicht verwenden können unkontrollierte Daten anzuzeigen. Mit anderen Worten, wenn Sie ein Skript schreiben und es externe Eingaben entgegennimmt (vom Benutzer als Argumente oder Dateinamen aus dem Dateisystem …), können Sie echo
nicht verwenden um es anzuzeigen.
Das ist in Ordnung:
echo >&2 Invalid file.
Dies ist nicht:
echo >&2 "Invalid file: $file"
(Obwohl es mit einigen (nicht UNIX-kompatiblen) echo
funktioniert Implementierungen wie bash
ist, wenn das xpg_echo
Option wurde auf die eine oder andere Weise nicht aktiviert, wie zum Zeitpunkt der Kompilierung oder über die Umgebung).
file=$(echo "$var" | tr ' ' _)
ist in den meisten Implementierungen nicht in Ordnung (Ausnahmen sind yash
mit ECHO_STYLE=raw
(mit der Einschränkung, dass yash
’s-Variablen können keine beliebigen Bytefolgen enthalten, also keine beliebigen Dateinamen) und zsh
's echo -E - "$var"
).
printf
, ist dagegen zuverlässiger, zumindest wenn es auf die grundlegende Verwendung von echo
beschränkt ist .
printf '%sn' "$var"
Gibt den Inhalt von $var
aus gefolgt von einem Zeilenumbruchzeichen, unabhängig davon, welches Zeichen es enthalten kann.
printf '%s' "$var"
Wird ohne das abschließende Zeilenumbruchzeichen ausgegeben.
Nun gibt es auch Unterschiede zwischen printf
Implementierungen. Es gibt einen Kern von Funktionen, der von POSIX spezifiziert wird, aber dann gibt es viele Erweiterungen. Einige unterstützen beispielsweise einen %q
um die Argumente zu zitieren, aber wie es gemacht wird, ist von Shell zu Shell unterschiedlich, manche unterstützen uxxxx
für Unicode-Zeichen. Das Verhalten variiert für printf '%10sn' "$var"
in Multibyte-Locales gibt es mindestens drei verschiedene Ergebnisse für printf %b '123'
Aber am Ende, wenn Sie sich an das POSIX-Feature-Set von printf
halten und versuchen Sie nicht, etwas zu Ausgefallenes damit zu machen, Sie haben keine Probleme mehr.
Denken Sie jedoch daran, dass das erste Argument das Format ist und daher keine variablen/unkontrollierten Daten enthalten sollte.
Ein zuverlässigeres echo
kann mit printf
implementiert werden , wie:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
Die Subshell (die in den meisten Shell-Implementierungen das Spawnen eines zusätzlichen Prozesses impliziert) kann mit local IFS
vermieden werden mit vielen Shells, oder indem Sie es so schreiben:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
if [ "$#" -gt 0 ]; then
printf ' %s' "[email protected]"
fi
fi
printf 'n'
}
Notizen
1. wie bash
's echo
Verhalten kann geändert werden.
Mit bash
, gibt es zur Laufzeit zwei Dinge, die das Verhalten von echo
steuern (neben enable -n echo
oder echo
neu definieren als Funktion oder Alias):
der xpg_echo
bash
Option und ob bash
befindet sich im Posix-Modus. posix
Modus kann aktiviert werden, wenn bash
wird als sh
aufgerufen oder wenn POSIXLY_CORRECT
in der Umgebung oder mit dem posix
ist Möglichkeit:
Standardverhalten auf den meisten Systemen:
$ bash -c 'echo -n "