Bedingte Pipe in Bash (benutzerdefiniertes Skript)

Bedingte Pipe in Bash (benutzerdefiniertes Skript)

Wie kann ich Befehle weiterleiten und im Skript entscheiden, ob die Pipe unterbrochen werden soll oder nicht?

Es ist ähnlich wiediese Frage. Aber stattdessen awkmöchte ich mein eigenes Skript verwenden:

check_ip | update_bind9

check_ipsollte die externe IP ( curl -s ifconfig.co) finden und sie mit der in einer Datei ( ~/.ip) gespeicherten IP vergleichen.

Hier ist die Hauptfrage:

Wenn sich die IP geändert hat, check_ipsollte die IP durch die Pipe geleitet werden. Wenn nicht, sollte die Pipe unterbrochen werden.

Unterbricht check_ipdas die Pipe oder update_bind9wird der Anruf ignoriert, wenn er nicht mit der IP aufgerufen wird?

Ich habe einige Tests durchgeführt und aufgerufen: check_ip | echo "ok".

Ich habe versucht exit 0, exit 1, , return true, return false. Habe versucht set -e, . Ohne Erfolg.

Antwort1

Alle Komponenten einer Pipelinegleichzeitig starten– Der zweite Befehl wird immer gestartet, unabhängig davon, was im ersten Befehl passiert.

Es ist also der zweite Befehl, der mit einer leeren Standardeingabe umgehen muss.


Allerdings würde ich sagen, dass Rohre hierfür ohnehin keine gute Wahl sind.

(Vielleicht verwechseln Sie |(Pipe) mit ||(Boolesches ODER)? LetzteresIstwird häufig als kürzere Alternative zu if/then verwendet, z. B. x || yruft y nur auf, wenn x fehlschlägt – und ähnlich: x && yruft y nur auf, wenn x erfolgreich ist.)

Im Allgemeinen sollten Sie einen if/then-Block verwenden. Wenn beispielsweise check_ipselbst 0 (Erfolg) zurückgibt, wenn die Adressen unterschiedlich sind, aber 1 (Fehler), wenn sie gleich sind, würde es folgendermaßen aussehen:

if addr=$(check_ip); then
    echo "$addr" | update_bind9
fi

Alternativ sähe es folgendermaßen aus, wenn update_bind9 die Adresse selbständig ermitteln kann (ohne stdin zu lesen):

if check_ip_needs_updating; then
    update_bind9
fi

Antwort2

Diese andere Antwortist gut. Wenn Sie jedoch wirklich bedingt weiterleiten müssen, finden Sie möglicherweiseifnenützlich:

ifneführt den folgenden Befehl genau dann aus, wenn die Standardeingabe nicht leer ist.

In Ihrem Fall wird es so sein check_ip | ifne update_bind9.

Höchstwahrscheinlich ifneist es in Ihrem Linux nicht standardmäßig installiert. In meinem Debian 10 ist es im moreutilsPaket enthalten. Man könnte meinen, ein zusätzliches Tool wäre völlig überflüssig, da wir die Ausgabe check_ipin einer Variablen speichern und bedingt wiederverwenden können, wie in der erwähnten Antwort:

if addr=$(check_ip); then
    echo "$addr" | update_bind9
fi

Die Bedingung kann sein [ -n "$addr" ], was auch immer; wichtig ist, dass wir eine Variable verwenden. Code wie der obige kann Ihr Problem lösen, aber im Allgemeinen gibt es mindestens drei Probleme damit. Nehmen Sie an, command1 | command2statt check_ip | update_bind9. Die Lösung wird verallgemeinert zu:

if foo=$(command1); then
    echo "$foo" | command2
fi

Und dann:

  1. foo=$(command1)entfernt alle nachfolgenden Zeilenumbrüche aus der Ausgabe von command1. Dann echo "$foo"(oderprintf … "$foo") fügt genau ein Zeilenumbruchzeichen (bzw. eine feste Anzahl davon) hinzu. Aus diesem Grund command2erhalten Sie möglicherweise nicht die exakte Ausgabe von command1.

  2. command1kann eine Ausgabe erzeugen, die NUL-Zeichen enthält. Bash kann NUL nicht in einer Variablen speichern (die meisten Shells können das nicht; zsh kann das). Wenn NUL erscheint, command2wird nicht die genaue Ausgabe von erhalten command1.

  3. command1kann einen riesigen oder sogar endlosen Datenstrom erzeugen, sodass das Speichern in einer Variablen möglicherweise nicht möglich ist.

Die Verwendung einer Datei anstelle der Variablen löst alle Probleme außer dem letzten. command1 | ifne command2ist gegen alle drei Probleme immun.

verwandte Informationen