GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum ist Printf besser als Echo?

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).

Verwandte:Verarbeiten Sie jede Ausgabezeile von "Ping" sofort in der Pipeline?

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 "
Linux
  1. [Linux]:Netzwerkprobleme mit MTR diagnostizieren – besser als Traceroute!

  2. Warum wird die Tilde (~) nicht innerhalb doppelter Anführungszeichen erweitert?

  3. Warum ist ein Linux-VPS die bessere Wahl als ein Windows-VPS?

  4. Was macht Cloud-Hosting besser als traditionelles VPS?

  5. Linux Vs Windows – Warum Linux besser für Programmierung und Webentwicklung ist

8 Gründe, warum Linux Mint für Linux-Anfänger besser ist als Ubuntu

11 Gründe, warum Linux besser ist als Windows

Linux vs. Mac:7 Gründe, warum Linux die bessere Wahl ist als Mac

Warum ist Linux in jeder Hinsicht so schlecht und Windows 11 besser?

Warum ist mmap() schneller als sequentielles IO?

Warum unterscheidet sich das „sudo“-Passwort vom „su root“-Passwort?