Ich habe eine Zeile im Bash-Skript gepippt und möchte überprüfen, ob die Pipe Daten enthält, bevor ich sie an ein Programm weitergebe.
Suche habe ich über gefunden, test -t 0
aber es funktioniert hier nicht. Gibt immer false zurück. Wie kann man also sicherstellen, dass die Pipe Daten enthält?
Beispiel:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
Ausgabe: fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
Ausgabe: fill
Im Gegensatz zu Standard / Canonical Weg, um zu testen, ob die vorstehende Pipeline Output produziert? Die Eingabe muss erhalten bleiben, damit sie an das Programm übergeben werden kann. Dies verallgemeinert, wie die Ausgabe von einem Prozess zu einem anderen weitergeleitet wird, aber nur ausgeführt wird, wenn der erste eine Ausgabe hat. Das konzentriert sich auf das Senden von E-Mails.
Antworten:
Es gibt keine Möglichkeit, den Inhalt einer Pipe mit den allgemein verfügbaren Shell-Dienstprogrammen zu überprüfen, noch gibt es eine Möglichkeit, ein Zeichen in die Pipe einzulesen und es dann zurückzusetzen. Die einzige Möglichkeit zu erkennen, dass eine Pipe Daten enthält, besteht darin, ein Byte zu lesen und dieses Byte dann an sein Ziel zu bringen.
Tun Sie einfach Folgendes: Lesen Sie ein Byte. Wenn Sie ein Dateiende erkennen, tun Sie, was Sie tun möchten, wenn die Eingabe leer ist. Wenn Sie ein Byte lesen, geben Sie an, was Sie tun möchten, wenn die Eingabe nicht leer ist, leiten Sie dieses Byte hinein und leiten Sie den Rest der Daten.
Das
ifne
Hilfsprogramm aus Joey Hess 'moreutils führt einen Befehl aus, wenn seine Eingabe nicht leer ist. Es wird normalerweise nicht standardmäßig installiert, sollte jedoch verfügbar sein oder auf den meisten Unix-Varianten einfach aufzubauen sein. Wenn die Eingabe leer ist,ifne
wird nichts ausgeführt und der Status 0 zurückgegeben, der nicht von dem Befehl unterschieden werden kann, der erfolgreich ausgeführt wird. Wenn Sie etwas tun möchten, wenn die Eingabe leer ist, müssen Sie dafür sorgen, dass der Befehl keine 0 zurückgibt. Dies kann erfolgen, indem der Erfolgsfall einen erkennbaren Fehlerstatus zurückgibt:test -t 0
hat nichts damit zu tun; Es wird geprüft, ob die Standardeingabe ein Terminal ist. Auf die eine oder andere Weise sagt es nichts darüber aus, ob ein Eingang verfügbar ist.quelle
peek
Dienstprogramm zu implementieren, das die tatsächlichen Daten aus einer Pipe zurückgeben könnte, und nicht nur, wie viel davon vorhanden ist. (In 4.4 BSD, 386BSD usw. wurden Pipes als Socket-Paare implementiert , aber das wurde in späteren Versionen von * BSD ausgeblendet - obwohl sie bidirektional gehalten wurden).read -t 0
(t bedeutet in diesem Fall Timeout, wenn Sie sich fragen).Eine einfache Lösung ist die Verwendung eines
ifne
Befehls (wenn die Eingabe nicht leer ist). In einigen Distributionen wird es nicht standardmäßig installiert. Es ist Teil des Paketsmoreutils
in den meisten Distributionen.ifne
Führt einen bestimmten Befehl genau dann aus, wenn die Standardeingabe nicht leer istBeachten Sie, dass die Standardeingabe, wenn sie nicht leer ist,
ifne
an den angegebenen Befehl übergeben wirdquelle
Alte Frage, aber für den Fall, dass jemand darauf stößt wie ich: Meine Lösung ist es, mit einer Auszeit zu lesen.
Wenn
stdin
leer, kehrt dies nach 5 Sekunden zurück. Andernfalls werden alle Eingaben gelesen und können nach Bedarf verarbeitet werden.quelle
-t
ist sie leider nicht Teil von POSIX: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.htmlüberprüfe ob der Dateideskriptor von stdin (0) offen oder geschlossen ist:
quelle
[ -t 0 ]
prüft, ob fd 0 bis zu einem tty geöffnet ist , nicht, ob es geschlossen oder offen ist../that_script </dev/null
=> "stdin has data". Oder./that_script <&-
um die stdin wirklich geschlossen zu haben .Sie können auch
test -s /dev/stdin
(in einer expliziten Subshell) verwenden.quelle
In der Bash:
Erkennt, ob eine Eingabe Daten enthält (ohne etwas zu lesen). Dann können Sie die Eingabe lesen (wenn die Eingabe zum Zeitpunkt der Ausführung des Lesevorgangs verfügbar ist):
Hinweis: Bitte beachten Sie, dass dies vom Timing abhängt. Dies erkennt, ob die Eingabe bereits Daten enthält, und zwar nur zu dem Zeitpunkt, zu dem sie ausgeführt wird
read -t
.Zum Beispiel mit
die Ausgabe ist
1
(Lesefehler, dh: leere Eingabe). Das Echo schreibt einige Daten, ist jedoch nicht sehr schnell zu starten und schreibt sein erstes Byte. Daherread -t 0
wird gemeldet, dass seine Eingabe leer ist, da das Programm noch nichts geschrieben hat.quelle
bash
wird entweder aselect()
oder aioctl(FIONREAD)
oder keiner von beiden ausgeführt, aber nicht beide, so wie es sollte, damit es funktioniert.read -t0
ist kaputt. Verwenden Sie es nichtEine einfache Möglichkeit, um zu überprüfen, ob Daten zum Lesen in Unix verfügbar sind, ist
FIONREAD
ioctl.Ich kann mir kein Standard-Dienstprogramm vorstellen, das genau das tut.
ifne
Deshalb hier ein triviales Programm, das dies tut (besser als das von moreutils IMHO ;-)).Wenn für stdin keine Daten verfügbar sind, wird es mit Status 1 beendet. Wenn Daten vorhanden sind, wird es ausgeführt
prog
. Wenn neinprog
angegeben wird, wird es mit dem Status 0 beendet.Sie können den
poll
Anruf entfernen , wenn Sie nur an sofort verfügbaren Daten interessiert sind. Dies sollte mit den meisten Arten von FDS funktionieren, nicht nur mit Pipes.fionread.c
quelle
POLLHUP
Ereignis warten , um den leeren Fall zu bearbeiten? Funktioniert das, wenn sich am anderen Ende der Pipe mehrere Dateibeschreibungen befinden?Wenn Sie kurze und kryptische Einzeiler mögen:
Ich habe die Beispiele aus der ursprünglichen Frage verwendet. Wenn Sie die weitergeleiteten Daten nicht möchten, verwenden Sie die
-q
Option grepquelle
Dies scheint eine vernünftige ifne-Implementierung in bash zu sein, wenn Sie mit dem Lesen der gesamten ersten Zeile einverstanden sind
quelle
read
Gibt auch false zurück, wenn die Eingabe nicht leer ist, aber kein Zeilenumbruchzeichen enthält,read
verarbeitet die Eingabe und liest möglicherweise mehr als eine Zeile, sofern Sie sie nicht als aufrufenIFS= read -r line
.echo
Kann nicht für beliebige Daten verwendet werden.Das funktioniert bei mir mit
read -rt 0
Beispiel aus der ursprünglichen Frage, ohne Daten:
quelle
{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
(falsch negativ) undtrue | { sleep .1; read -rt0 && echo YES; }
(falsch positiv). In der Tat, bashread
wird sogar von fds eröffnet täuschen Nur - Schreib- Modus:{ read -rt0 && echo YES; cat; } 0>/tmp/foo
. Das einzige, was es zu tun scheint, ist einselect(2)
auf diesem fd.select
gibt ein fd als "ready" zurück, wenn einread(2)
darauf nicht blockiert würde, egal ob es zurückkommtEOF
oder ein Fehler vorliegt. Fazit:read -t0
ist gebrochen inbash
. Benutze es nicht.{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
ist kein falsches Negativ, da (zum Zeitpunkt der Ausführung des Lesevorgangs ) keine Eingabe erfolgt. Später (sleep 0,1) , die Eingabe ist verfügbar (für die Katze).echo "" | { read -t0 && echo YES; }
JA aus,echo "" | { read -rt0 && echo YES; }
aber nicht.