Cat hängt beim Versuch, leere STDIN zu lesen

410
StarCrashr

Mein Skript versucht, Informationen zu sammeln, die möglicherweise zur Ausführungszeit in STDIN vorhanden sind oder nicht, aber cat hängt, wenn die Pipe leer ist. Wie kann ich sicherstellen, dass mein Skript diesen Schritt überspringt, wenn in STDIN nichts enthalten ist?

stdin=$(cat <&0) 

Beachten Sie, dass ich speziell nicht nach Lösungen suche, die sich auf / dev / beziehen, da ich beabsichtige, dass dies in einer Chroot-Umgebung verwendbar ist, unabhängig davon, ob / dev / wenn möglich gemountet wurde.

2
Ich wäre überrascht, wenn eine Chroot ohne _im least_ / dev / null in der Produktion verwendbar wäre. Es gibt einen weiten Weg zwischen "/ dev mount" und "/ dev manuell erstellt mit dem Minimum", sowieso ... grawity vor 5 Jahren 0
Keine Lösung, aber "Katze" oder sogar eine einfache "Katze" würde das Gleiche tun wie Ihre Katze <& 0 ". xenoid vor 5 Jahren 0
@xenoid ja, ich weiß. Ich habe alle drei ausprobiert und sie liefern alle identische Ergebnisse. <& 0 ist nur noch ein wenig übrig, um zu sehen, ob ich Katzenfehler machen kann, anstatt zu warten. StarCrashr vor 5 Jahren 0
Übrigens spielte ich weiter, während ich auf eine Antwort wartete, und stellte fest, dass sie nicht nur hängen blieb. Es suchte nach Eingaben von der Konsole. Dies führte dazu, dass mit Heredocs experimentiert wurde, um zu versuchen, die Katze zu beenden, wenn keine Eingabe erfolgt, aber eine Antwort erhalten wurde, bevor sie damit weiterkam. StarCrashr vor 5 Jahren 0

1 Antwort auf die Frage

3
Kamil Maciorowski

Normalerweise hat ein erschöpftes Rohr beim Arbeiten mit Rohren und Standardleitungen keine besondere Bedeutung. Neue Daten werden möglicherweise weiterhin angezeigt, bis eofdie Pipe geschlossen wird. Sie catendet eofwie erwartet. Wenn es vor keine Daten sind eof, nur dann können Sie die stdin sagen waren wirklich leer.

Betrachten sender | receiver. Es ist nicht ungewöhnlich, dass das sender(viel) langsamer ist als das receiver; In diesem Fall ist der receiverStandardwert fast immer erschöpft, aber Sie möchten fast nie die gesamte Pfeife töten. Daher sind Werkzeuge, die auf "empty" (erschöpft, aber noch nicht beendet) stdin stehen, eher Ausnahmen als Standard.

In Bash gibt es read -t 0( -twird von POSIX nicht benötigt). Von help read:

Wenn TIMEOUTist 0, readkehrt sofort zurück, ohne zu versuchen, alle Daten zu lesen, Erfolg Rückkehr nur, wenn der Eingang auf dem angegebenen Dateideskriptor verfügbar ist.

Standardmäßig wird readvon stdin gelesen. Der Exit-Status von read -t 0sagt Ihnen dann, ob der stdin "leer" ist. Aber Vorsicht! Ein Befehl wie

echo 1 | read -t 0 

kann erfolgreich beendet werden oder nicht, weil echound readgleichzeitig ausgeführt werden, nicht sequentiell. Um dies zu vermeiden, sollte Ihr Skript sleepvorher eine Weile dauern read -t 0. Abhängig davon, woher der Standard kommt, kann "eine Weile" relativ lang sein. Tun Sie so etwas:

sleep 1 if read -t 0; then … # process stdin here, you know it's non-empty 

Sie füllen eine Variable mit Daten aus stdin. Da das Speichern von binären Daten in einer Variablen keine gute Idee ist (lesen Sie dies ), sind Ihre Daten möglicherweise nur Text. Wenn ja, so verwenden read -t:

read -r -t 5 -d $'\0' stdin 

Ein Nullzeichen (das Sie in einer Bash-Variablen sowieso nicht speichern können) als Trennzeichen ( -d $'\0') ermöglicht Ihnen das Lesen von Text (z. B. mit Zeilenumbrüchen) der stdinVariablen. Nach höchstens 5 Sekunden ( -t 5) wird der Befehl beendet, damit das Skript fortgesetzt werden kann.

Ein anderer Ansatz ist mit timeout. Ein grundlegendes Beispiel aus meinem Debian:

timeout --foreground 5 cat | wc -c 

(Ersetzen Sie wc -cdurch Ihren Code, der stdin parst; es ist nur ein Beispiel).

Dies sollte gut mit binären Daten umgehen. Wenn catnicht, eofdann nach 5 Sekunden, wird es getötet, so wcwird es eoftrotzdem und die Linie bleibt stehen. Das Problem ist cat, dass das Problem beendet wird, unabhängig davon, ob gerade Daten verarbeitet werden. Ich kann mir vorstellen, dass Sie alle Daten erhalten möchten, wenn nur einige vorhanden sind, selbst wenn dies mehr als 5 Sekunden dauert. Verbesserte Version:

{ timeout --foreground 5 dd bs=1 count=1 2>/dev/null && cat; } | wc -c 

Wenn das erste Byte innerhalb von 5 Sekunden erscheint, catwird es ausgelöst. Es werden dann alle weiteren Eingaben verarbeitet eof, unabhängig davon, wie lange es dauert. Alles, einschließlich des ersten Bytes (falls vorhanden) wird zu gehen wc. Wenn es weder ein Byte noch eofin 5 Sekunden gibt, wcerhalten Sie nur eof; Die Linie bleibt stehen.

Meine Eingabe ist in der Tat Text, falls jemand neugierig ist. Es ist ein durch Doppelpunkte getrenntes Feld von Informationen über einen Benutzer und / oder einen Host, das ein Kennwort enthalten kann oder nicht. Aus diesem Grund ist optionales stdin für meine Verwendung wichtig (in der Befehlszeile sind keine Kennwörter zulässig). Wenn in stdin keine Daten vorhanden sind, versucht das Skript kennwortlose Authentifizierungsmethoden und sucht stattdessen nach den restlichen Informationen im ersten Argument. StarCrashr vor 5 Jahren 0