$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
Hier fortune
bedingtes Programm ohne exit(rand())
.
Können Sie erklären? Was ist hier los??
Lyrisch-historischer Exkurs
Ich habe diesen Heisenbug vor einem Vierteljahrhundert zum ersten Mal kennengelernt. Dann war es für das Gateway in FaxNET notwendig, mehrere Dienstprogramme über zu erstellen
Meine früheren Erfahrungen im Umgang mit Fehlern in sendmail und uucp/uupc trugen zu meiner Sorgfalt bei der „gründlichen Fehlerbehandlung“ bei. Es macht keinen Sinn, in die Details dieser Geschichte einzutauchen, aber ich habe zwei Wochen lang 10 bis 14 Stunden lang mit diesem Heisenbug zu kämpfen. Deshalb wurde daran erinnert und gestern kam dieser alte Bekannte erneut zu Besuch vorbei.
TL;DR Antwort
Dienstprogramm head
können Schließen Sie den Kanal ab fortune
auf einmal sobald er die erste Zeile liest. Wenn fortune
Gibt mehr als eine Zeile aus, dann der entsprechende Aufruf write()
gibt einen Fehler zurück oder meldet, dass weniger Bytes ausgegeben werden als angefordert. Im Gegenzug mit sorgfältiger Fehlerbehandlung geschrieben fortune
hat das Recht, diese Situation in seinem Austrittsstatus widerzuspiegeln. Dann aufgrund der Installation set -o pipefail
wird funktionieren || echo "Вы проиграли"
.
Jedoch head
wird es vielleicht nicht rechtzeitig schaffen vorher schließen fortune
beendet die Ausgabe der Daten. Dann wird es funktionieren && echo "Повезло!"
.
In einem meiner heutigen GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
Ins Menschliche übersetzt
Es ist hier üblich für --version
Es fragt, wer er ist, und wenn die Option nicht unterstützt wird, wird ein Stub eingefügt „Bitte verwenden Sie einen GCC- oder CLANG-kompatiblen Compiler“.
Ähnlich
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
Ehrlich gesagt habe ich meinen alten „Bekannten“ nicht sofort wiedererkannt. Darüber hinaus wurde das Projekt bereits viele Male auf Elbrus und unter vielen verschiedenen Distributionen, einschließlich Alt, getestet. Mit verschiedenen Compilern, Versionen von GNU Make und Bash. Deshalb wollte ich meinen Fehler hier nicht sehen.
Beim Versuch, das Problem zu reproduzieren und/oder zu verstehen, was vor sich ging, begannen noch seltsamere Dinge zu passieren.
Befehlszeilenzauber:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
Hin und wieder wurde zusätzlicher Text erzeugt, dann aber nicht ... Oft blieb eine der Optionen ziemlich lange hängen, aber wenn man länger herumstocherte, bekam man immer beides!
Natürlich strace
Übrigens, wie jeder, der etwas auf sich hält strace
zieht es vor, sich nicht zu reproduzieren.
Was ist denn los?
- Dienstprogramm
head
hat das Recht (bzw. wird sogar gezwungen), den gelesenen Kanal zu schließen, sobald er die angeforderte Anzahl Zeilen liest. - Der datengenerierende Programmschreiber (in diesem Fall
cc
) können mehrere Zeilen drucken und frei Tun Sie dies durch mehrere Anrufewrite()
. - wenn Der Leser hat Zeit, den Kanal auf seiner Seite zu schließen, bevor die Aufzeichnung auf der Seite des Autors endet. Dann erhält der Autor eine Fehlermeldung.
- Autorenprogramm kann Beide ignorieren den Kanalschreibfehler und geben ihn in Ihrem Abschlusscode wieder.
- Aufgrund der Installation
set -o pipefail
Der Pipeline-Abschlusscode ist ungleich Null (fehlerhaft), wenn das Ergebnis von mindestens einem Element ungleich Null ist, und dann funktioniert es|| echo "Please use GCC or CLANG compatible compiler"
.
Abhängig davon, wie das Schreibprogramm mit Signalen arbeitet, kann es zu Abweichungen kommen. Beispielsweise kann das Programm abnormal beendet werden (mit automatischer Generierung eines Beendigungsstatus ungleich Null/Fehler) oder write()
gibt das Ergebnis zurück, dass weniger Bytes geschrieben wurden als angefordert und festgelegt errno = EPIPE
.
Wer ist schuld?
Im beschriebenen Fall ein bisschen von allem. Fehlerbehandlung in cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) nicht überflüssig. In vielen Fällen ist es besser, auf Nummer sicher zu gehen, auch wenn dadurch plötzliche Mängel in einem auf traditionelles Verhalten ausgelegten Boilerplate aufgedeckt werden.
Was zu tun ist?
Falsch:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
Richtig:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
Hier wird das vorzeitige Schließen einer Pipe vom Befehlsinterpreter gehandhabt, wenn die verschachtelte Pipeline („innerhalb“ der Klammern) bedient wird. Dementsprechend, wenn
fortune
meldet einen Fehler beim Schreiben in einen geschlossenen Kanal im Status und dann in der Ausgabe|| echo "Ошибка"
es wird nicht weiterkommen, da der Kanal bereits geschlossen ist. -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
Hier ist das Dienstprogramm
cat
wirkt als Dämpfer, da es den Fehler ignoriertEPIPE
beim Rückzug. Dies reicht für die Schlussfolgerungfortune
klein (mehrere Zeilen) und passt in den Kanalpuffer (von 512 Byte bis ≈64 KB, in den meisten Betriebssystemen ≥ 4 KB). Andernfalls kann das Problem erneut auftreten.
So verarbeiten Sie es richtig EPIPE
und andere Aufnahmefehler?
Es gibt keine allgemeingültige Lösung, aber einfache Empfehlungen:
EPIPE
erforderlich müssen bearbeitet werden (und spiegelt sich im Exit-Status wider), wenn Daten ausgegeben werden, die Integrität erfordern. Zum Beispiel beim Betrieb von Archivierern oder Backup-Dienstprogrammen.EPIPE
besser ignorieren bei der Anzeige von Informationen und Zusatzmeldungen. Zum Beispiel bei der Anzeige von Informationen zu Optionen--help
oder--version
.- Wenn der zu entwickelnde Code zuvor in einer Pipeline verwendet werden kann
| head
dannEPIPE
Es ist besser, es zu ignorieren, andernfalls ist es besser, es zu verarbeiten und im Exit-Status zu reflektieren.
Ich möchte diese Gelegenheit nutzen, um den Teams meinen Dank auszusprechen
Weiter so, Kameraden, weiter so
Danke
KDPV von
Source: habr.com