Wie unterdrückt man Fehlermeldungen in zsh?

Wie unterdrückt man Fehlermeldungen in zsh?

In zsh

  • rm foo.bardruckt rm: foo.bar: No such file or directory.
  • rm foo.bar 2>/dev/nulldruckt nichts, wie ich erwartet habe.

Wenn der Befehl jedoch Musterübereinstimmungen enthält, wird der Fehler nicht durch Folgendes unterdrückt 2>/dev/null:

  • rm *.bardruckt zsh: no matches found: *.bar.
  • rm *.bar 2>/dev/nulldruckt das gleiche.

Gibt es eine generelle Möglichkeit, Fehlermeldungen in zsh zu unterdrücken?einfachMethode für alle Arten von Fehlermeldungen.

Antwort1

Wenn Sie versuchen, die Datei auszuführen rm *.bar, und keine passende Datei vorhanden ist *.bar, können grundsätzlich zwei Dinge passieren:

  • Das POSIX-Verhalten besteht darin, dass die Shell rmmit literal *.barals Argument ausgeführt wird. Das Tool versucht, eine Datei mit dem wörtlichen Namen zu entfernen *.bar(als ob Sie ausführen rm '*.bar'), es schlägt fehl und gibt etwas wie aus rm: *.bar: No such file or directory. So shverhalten sich die POSIX-Shell ( ) und kompatible Shells.

  • Nicht-POSIX-Verhalten bedeutet, dass die Shell erkennt, dass keine Übereinstimmung vorliegt, etwas wie ausgibt no matches found: *.barund nicht ausführtrm überhauptSo verhält sich Zsh standardmäßig. (Zum Vergleich: In Bash können Sie mit auf dieses Verhalten umschalten shopt -s failglob.)

Ein Fehler von rm(im ersteren Fall) wird auf dem Stderr von ausgegeben rm. Ein Fehler von der Shell (im letzteren Fall) wird auf dem Stderr der Shell ausgegeben.

Die Umleitung betrifft nur rm *.bar 2>/dev/nullden Standardfehler von + . In Ihrem Beispiel läuft es nicht einmal.rmrm

Um den stderr der Hauptshell umzuleiten, benötigen Sie exec. Mit „der Hauptshell“ meine ich die interaktive Shell, in der Sie tippen; oder, wenn Sie ein Skript ausführen, die Shell, die das Skript interpretiert. Beispiel:

exec 2>/dev/null

In Zsh können Sie es sogar schließen:

exec 2>&-

Ein sinnvoller Ansatz besteht darin, den ursprünglichen stderr vorher zu duplizieren, nur für den Fall, dass Sie ihn später verwenden (oder wiederherstellen) müssen. Beispiel (beachten Sie, wenn Sie diesen Code in ein interaktives Zsh einfügen möchten, sollten Sie aufrufensetopt interactive_commentsErste):

exec 7>&2           # "save" stderr
exec 2>/dev/null    # redirect
rm *.bar
whatever
exec 2>&7           # restore
exec 7>&-           # close descriptor which is no longer needed

Hier 7handelt es sich um eine beliebige einstellige Zahl, die sonst nicht als Dateideskriptor verwendet wird. Die ersten beiden Zeilen können als eine Zeile geschrieben werden: exec 7>&2 2>/dev/null; ebenso die letzten beiden Zeilen.

Beachten Sie rm, dass jeder Befehl (wie whatever), der ohne stderr-Umleitung aufgerufen wird, stderr von der Shell erbt. Dies bedeutet im obigen Beispiel rm(falls es jemals ausgeführt wird) und whateverdie Fehlermeldungen (falls vorhanden) an ausgibt /dev/null. Danach exec 2>/dev/nullkönnen Sie eine beliebige Anzahl von Befehlen aufrufen und alle haben /dev/nullstderr als ihren Befehl. Wenn Sie wirklich alle Arten von Fehlermeldungen unterdrücken möchten, ist dies der richtige Weg.

Die Lösung funktioniert in vielen Shells, nicht nur in Zsh. Wenn Sie stderr einer interaktiven Shell umleiten möchten, denken Sie daran, dass einige weniger anspruchsvolle Shells stderr verwenden, um ihre Eingabeaufforderung zu drucken.

Beachten Sie, dass ein Prozess seinen eigenen stderr umleiten kann (wie unsere Shell es mit tut exec 2>…); oder er kann Fehlermeldungen auf stdout oder auf ausgeben /dev/tty(sollte er nicht, kann es aber technisch). exec 2>/dev/nullDahernichtgarantieren, dass Sie keine Nachrichten sehen, die wie Fehlermeldungen aussehen.

Danach können Sie stderr von jedem Befehl bei Bedarf umleiten. Wenn wir exec 2>/dev/nullbeispielsweise stattdessen Folgendes hätten:whatever

whatever 2>&7

dann würden seine Fehlermeldungen an die ursprüngliche Standardfehlermeldung gehen, die wir absichtlich als Dateideskriptor gespeichert haben 7.


execrmist nicht die einzige Möglichkeit, stderr (oder einen anderen Dateideskriptor) einer Shell umzuleiten. Sie können eine Shell (allerdings nicht die gesamte Haupt-Shell) wie in behandeln rm … 2>/dev/null. Sie können eine Subshell wie folgt behandeln:

( rm *.bar ) 2>/dev/null

oder einfach ein von der Haupt-Shell interpretierter Code:

{ rm *.bar; } 2>/dev/null

( ;hier ist es in Zsh optional, in einigen anderen Shells obligatorisch; ich wollte nur, dass der Code auch außerhalb von Zsh funktioniert).

Jede dieser Zeilen kann Ihr Problem lösen. Im Kontext der Unterdrückung von Fehlermeldungen aus einer Shell sind die beiden Zeilen gleichwertig ++ .

Beachten Sie, dass die Umleitung bei (/ beginnt und bei / {endet . Ihr Umfang ist begrenzt. Sie können dennoch viele Befehle in die Klammern setzen, sodass der „begrenzte“ Umfang tatsächlich das gesamte Skript umfassen kann. Oder es kann nur ein Teil des Skripts sein (einschließlich der zuvor in dieser Antwort eingeführten, was auch immer Sie wollen). Die Tatsache, dass die Umleitung nach / nicht mehr funktioniert, macht diesen Ansatz zu einer guten Alternative zum Speichern und Wiederherstellen von stderr mit .)}whatever)}exec


+ Genau genommen: es betrifft " rm" bevor und nachdem es zu (oder versucht zu werden) rm. Habt Geduld mit mir. Die Umleitung in rm … 2>/dev/nullbeginnt als Umleitung, die von einemrmgegabelte Shell, die sich selbst durch eine ausführbare Datei ersetzen wird. Diese Shell leitet ihren eigenen stderr um, bevor sie versucht, sich durch zu ersetzen rm. Jeder Fehler, den sie nach der Umleitung meldet, wird an weitergeleitet /dev/null. Wenn beispielsweise rmnicht gefunden werden kann, wird die bereits durchgeführte Umleitung den command not foundFehler unterdrücken.

++ Technisch gesehen ( … )erstellt es eine Subshell, { … }tut es aber nicht. Eine Subshell verhält sich wie ein separater Shell-Prozess, der Variablen und das aktuelle Arbeitsverzeichnis erbt, aber nichts (wie Variablen oder das aktuelle Arbeitsverzeichnis) in seiner übergeordneten Shell ändern kann. Sie kann als wirklich separater Prozess realisiert werden oder nicht, es kommt auf das Verhalten an. Andererseits { … }gruppiert es Befehle nur für einen bestimmten Zweck (in unserem Fall ist der Zweck die Umleitung). Das bedeutet nicht, dass es nie eine Subshell gibt, wenn Sie verwenden { … }; z. B. sind in einer Pipeline alle Teile außer dem letzten sowieso Subshells (in einigen Shells: alle Teile einschließlich des letzten). Diese technischen Details sind im Kontext unseres Problems allein irrelevant, aber im Allgemeinen können sie einen Unterschied machen.

verwandte Informationen