Wie kann ich einen Rohrbruchfehler beheben?

36

Ich habe kürzlich RVM (gemäß den Anweisungen unter http://rvm.io ) nach einer Neuinstallation von Ubuntu 12.10 neu installiert, als ich ein SSD-Laufwerk bekam.

Wenn ich jetzt tippe: type rvm | head -1

Ich erhalte folgende Fehlermeldung:

rvm is a function
-bash: type: write error: Broken pipe

Aber wenn ich den Befehl sofort wiederhole, erhalte ich nur:

rvm is a function

Und es scheint alles in Ordnung zu sein? Was ist los? Was kann ich tun, um das Problem zu beheben? Das passiert nicht immer. Es scheint eher sporadisch zu sein. Ich habe versucht, ein Muster dafür zu finden, habe es aber noch nicht gefunden.

Jason Shultz
quelle

Antworten:

57

"Rohrbruch" in dieser Situation zu sehen, ist selten, aber normal.

Wenn Sie ausführen type rvm | head -1, wird die Bash type rvmin einem Prozess head -1in einem anderen ausgeführt. 1 Der stdout von typeist mit dem "write" -Ende einer Pipe verbunden , der stdin headmit dem "read" -Ende. Beide Prozesse laufen gleichzeitig ab.

Der head -1Prozess liest Daten aus stdin (normalerweise in Stücken von 8 kB), druckt eine einzelne Zeile aus (je nach -1Option) und wird beendet, wodurch das "Lese" -Ende der Pipe geschlossen wird. Da die rvmFunktion ziemlich lang ist (etwa 11 kB, nachdem sie durch Bash analysiert und rekonstruiert wurde), bedeutet dies, dass sie beendet wird, headwährend typenoch ein paar kB Daten zum Schreiben vorhanden sind.

Da typeversucht wird, in eine Pipe zu schreiben, deren anderes Ende geschlossen wurde - eine unterbrochene Pipe -, gibt die Funktion write (), die sie aufgerufen hat, einen EPIPE-Fehler zurück, der als "unterbrochene Pipe" übersetzt wird. Zusätzlich zu diesem Fehler sendet der Kernel auch das Signal SIGPIPE an type, wodurch der Prozess standardmäßig sofort abgebrochen wird .

(Das Signal ist in interaktiven Shells sehr nützlich, da die meisten Benutzer nicht möchten, dass der erste Prozess weiterläuft und versucht, ins Nirgendwo zu schreiben. In der Zwischenzeit ignorieren nicht interaktive Dienste SIGPIPE - es wäre nicht gut, wenn ein Daemon lange läuft bei so einem einfachen Fehler sterben - so finden sie den Fehlercode sehr nützlich.)

Die Signalübermittlung erfolgt jedoch nicht zu 100% sofort, und es kann Fälle geben, in denen write () EPIPE zurückgibt und der Prozess vor dem Empfang des Signals eine kurze Zeit lang weiterläuft. In diesem Fall typebleibt genügend Zeit, um den fehlgeschlagenen Schreibvorgang zu bemerken, den Fehlercode zu übersetzen und sogar eine Fehlermeldung an stderr zu drucken, bevor er von SIGPIPE beendet wird. (Die Fehlermeldung lautet "-bash: type:", da dies typeein integrierter Befehl von bash selbst ist.)

Dies scheint auf Multi-CPU-Systemen häufiger zu sein, da der typeProzess und der Signalübermittlungscode des Kernels buchstäblich gleichzeitig auf verschiedenen Kernen ausgeführt werden können.

Es wäre möglich, diese Nachricht zu entfernen, indem Sie den typeeingebauten Code (im Bash-Quellcode) patchen , damit er sofort beendet wird, wenn er eine EPIPE von der write () -Funktion empfängt.

Es gibt jedoch keinen Grund zur Sorge und es hängt rvmin keiner Weise mit Ihrer Installation zusammen.

Grawity
quelle
Vielen Dank! Ich habe mir darüber Sorgen gemacht. Ich habe letzte Nacht ungefähr eine Stunde lang gegoogelt, um meine rvm-Installation zu reparieren und zu reparieren. Ich hatte gerade ein SSD-Laufwerk ausgetauscht und LVM verwendet und die Festplatte verschlüsselt, sodass viele Variablen ins Spiel kamen, und ich war mir einfach nicht sicher, was seitwärts gegangen sein könnte. Danke, dass du mich beruhigt hast!
Jason Shultz
Ich habe jahrelang die Ausgabe von lsthrough weitergeleitet head -1, und heute erhalte ich eine kaputte Pipe-Meldung.
Tulains Córdova
1
(Hinweis: Der Fehler "Pipe defekt " kommt nicht vom Signal. Er kommt vom Fehler . Obwohl die Shell ansonsten möglicherweise Textnachrichten für signalinduzierte Exits anzeigt, ist sie normalerweise intelligent genug, um vorzutäuschen, dass ein SIGPIPE-Exit a war "sauber" ein.)
Grawity
23

Sie können einen Rohrbruch auf Kosten eines anderen Prozesses beheben, indem Sie tail -n +1in Ihr Rohr Folgendes einfügen :

Typ rvm | tail -n +1 | Kopf -1

Das +1weist Sie tailan, die erste Eingabezeile und alles, was folgt , zu drucken. Die Ausgabe ist genau so, als wäre sie tail -n +1nicht vorhanden, aber das Programm ist intelligent genug, um die Standardausgabe zu überprüfen und die Pipe sauber zu schließen. Keine kaputten Rohre mehr .

Huuu
quelle
1
Guter Trick. Ich habe in einer anderen Situation als der hier zur Verfügung gestellten verwendet. Vielen Dank!
Jemand verwendet Sie immer noch MS-DOS
6
Sah nach einer großartigen Lösung aus, aber auf Ubuntu 14.04.2 mit Tail 8.21 erhalte ich "Tail: Schreibfehler: Rohrbruch", was keine Verbesserung darstellt.
Roger Dueck
2
@ RogerDueck ist richtig. Ich sehe das auch bei einem Mandriva-System, das für eine ähnliche Art von Problem find /var/lib/mysql -xdev -type f -daystart -mmin +5 -print0 | xargs -0 ls -ldt | tail -n +1 | headzuverlässig ergibt xargs: ls: terminated by signal 13. Wie wir wissen, liegt das Problem in der Erschöpfung der Eingabe und es gibt wirklich nur einen Befehl, der sich mit dem Puffern befasst: dd. Das Hinzufügen | dd obs=1Mzur Pipeline behebt die SIGPIPE für meinen Anwendungsfall.
Andrew Beals
3
Ich werde weiter mein Vorschlag ändern, obwohl ich anmerken , dass ich glaube nicht , dass xargs oder Typ sollte kvetching über SIGPIPE sein, dazu: type rvm | (head -1 ; dd of=/dev/null) Das ist natürlich, für andere Vorschläge ähnlich ist , wie es alle Eingangs verursacht verarbeitet werden , ddsollte aber das effizienteste Programm sein, um mit solchen Dingen umzugehen.
Andrew Beals
3
Kommentar zu SIGPIPE-Verstößen hier: mail-index.netbsd.org/tech-userlevel/2013/01/07/msg007110.html
Andrew Beals
2

Die write error: Broken pipeNachricht bezieht sich auf einen Schreibvorgang, bei dem versucht wird, in eine Pipe zu schreiben, ohne dass am Leseende dieser Pipe noch Leser übrig sind, und auf den besonderen Umstand, dass das SIGPIPESignal so eingestellt ist, dass es entweder vom aktuellen oder vom übergeordneten Prozess ignoriert wird. Wenn es der übergeordnete Prozess war, SIGPIPEder ignoriert wurde, kann der untergeordnete Prozess dies in einer nicht-interaktiven Shell nicht wieder rückgängig machen.

Es ist jedoch möglich, das Programm mit expliziten Subshells zu beenden , type rvmwenn es head -1beendet wird. Auf diese Weise können wir einen Hintergrund erstellen, eine Nachricht an die Subshell type rvmsenden und dort eine Trap implementieren , um explizit zu töten .typepidhead -1EXITtype rvm

trap "" PIPE        # parent process sets SIGPIPE to be ignored
bash                # start child process
export LANG=C
# create a fake rvm function
eval "
rvm() {
$(printf 'echo line of rvm code %s\n' {1..10000})
}
"

# rvm is a function
# bash: type: write error: Broken pipe
type rvm | head -1

# kill type rvm when head -1 terminates
# sleep 0: do nothing but with external command
( (sleep 0; type rvm) & echo ${!} ; wait ${!} ) | 
    (trap 'trap - EXIT; kill "$typepid"; exit' EXIT; typepid="$(head -1)"; head -1)
zancox
quelle
Aus der Antwort von grawity: Sie haben typegenügend Zeit, um den fehlgeschlagenen Schreibvorgang zu bemerken, den Fehlercode zu übersetzen und sogar eine Fehlermeldung an stderr zu drucken, bevor Sie von SIGPIPE getötet werden . Ich denke, Ihre Lösung hindert den Producer- Prozess ( typehier) nicht daran, auf das fehlgeschlagene Schreiben (aufgrund einer geschlossenen Pipe) zu reagieren, oder?
Piotr Dobrogost