Jak mogę naprawić błąd Broken Pipe?

36

Niedawno ponownie zainstalowałem RVM (postępując zgodnie z instrukcjami na http://rvm.io ) po nowej instalacji Ubuntu 12.10, kiedy dostałem dysk SSD.

Teraz, kiedy piszę: type rvm | head -1

Otrzymuję następujący błąd:

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

Ale jeśli natychmiast powtórzę polecenie, otrzymam tylko:

rvm is a function

I wygląda na to, że wszystko jest w porządku? Co się dzieje? Co mogę zrobić, aby to naprawić? Nie zawsze tak się dzieje. Wydaje się być bardziej sporadyczny. Próbowałem znaleźć jakiś wzór, ale jeszcze tego nie zrobiłem.

Jason Shultz
źródło

Odpowiedzi:

57

Widzenie „Broken pipe” w tej sytuacji jest rzadkie, ale normalne.

Po uruchomieniu type rvm | head -1bash wykonuje się type rvmw jednym procesie, head -1w innym. 1 Stdout typejest podłączony do końca „zapisu” potoku , a stdin headdo końca „odczytu”. Oba procesy działają w tym samym czasie.

head -1Proces odczytuje dane ze standardowego wejścia (zwykle kawałki 8 KB) drukuje jednej linii (w zależności od -1wariantu) i wyjścia, co powoduje „Czytaj” koniec rury, która ma być zamknięty. Ponieważ rvmfunkcja jest dość długa (około 11 kB po przeanalizowaniu i zrekonstruowaniu przez bash), oznacza to, że headkończy działanie, typewciąż mając kilka kB danych do zapisania.

W tym momencie, ponieważ typepróbuje napisać do potoku, którego drugi koniec został zamknięty - zepsuty potok - funkcja write (), którą skalował, zwróci błąd EPIPE, przetłumaczony jako „zepsuty potok”. Oprócz tego błędu jądro wysyła również sygnał SIGPIPE type, który domyślnie zabija proces natychmiast.

(Sygnał jest bardzo przydatny w interaktywnych powłokach, ponieważ większość użytkowników nie chce, aby pierwszy proces działał dalej i próbował pisać do nikąd. Tymczasem usługi nieinteraktywne ignorują SIGPIPE - nie byłoby dobrze, aby długo działający demon umierają na tak prosty błąd - dlatego uważają, że kod błędu jest bardzo przydatny).

Jednak dostarczanie sygnału nie jest w 100% natychmiastowe i mogą wystąpić przypadki, gdy write () zwraca EPIPE, a proces kontynuuje działanie przez krótką chwilę przed otrzymaniem sygnału. W takim przypadku typedostaje wystarczająco dużo czasu, aby zauważyć nieudany zapis, przetłumaczyć kod błędu, a nawet wydrukować komunikat o błędzie do stderr, zanim zostanie zabity przez SIGPIPE. (Komunikat o błędzie brzmi „-bash: type:”, ponieważ typejest to wbudowane polecenie samego basha.)

Wydaje się, że jest to bardziej powszechne w systemach wieloprocesorowych, ponieważ typeproces i kod dostarczania sygnału jądra mogą działać na różnych rdzeniach, dosłownie w tym samym czasie.

Byłoby możliwe usunięcie tego komunikatu przez załatanie typewbudowanego (w kodzie źródłowym bash), aby natychmiast wyjść, gdy otrzyma EPIPE z funkcji write ().

Nie należy się jednak tym przejmować i nie ma to żadnego związku z rvminstalacją.

grawitacja
źródło
Dziękuję Ci! Martwiłem się tym. Wczoraj wieczorem zrobiłem około godziny googlingu, szukając rozwiązania mojej instalacji RVM i naprawiając go, próbując go naprawić. Właśnie wymieniłem się dyskiem SSD i korzystałem z LVM i zaszyfrowałem dysk twardy, więc w grę wchodziło wiele zmiennych i po prostu nie byłem pewien, co mogło pójść na bok. Dziękuję za uspokojenie mojego umysłu!
Jason Shultz
Przesyłam dane wyjściowe lsprzez head -1lata, a dziś otrzymuję komunikat zepsutej rury.
Tulains Córdova
1
(Uwaga: błąd „Broken pipe” nie pochodzi z sygnału. Pochodzi z errno . Podczas gdy powłoka może w przeciwnym razie wyświetlać wiadomości tekstowe dla wyjść indukowanych sygnałem, zwykle jest wystarczająco mądra, aby udawać, że wyjście SIGPIPE było „czysty”).
grawity
23

Możesz naprawić zepsutą rurę kosztem innego procesu , wstawiając tail -n +1do niej rurkę w następujący sposób:

wpisz rvm | ogon -n +1 | głowa -1

+1Mówi taildo wydrukowania pierwszej linii wejścia i wszystkiego, co następuje. Dane wyjściowe będą dokładnie takie same, jak gdyby ich tail -n +1nie było, ale program jest wystarczająco inteligentny, aby sprawdzić standardowe dane wyjściowe i dokładnie zamyka potok. Nigdy więcej połamanych rur .

Huuu
źródło
1
Niezła sztuczka. Użyłem w innej sytuacji niż tutaj. Dzięki!
Ktoś nadal cię używa MS-DOS
6
Wyglądało to na świetne rozwiązanie, ale na Ubuntu 14.04.2 z ogonem 8.21 pojawia się „ogon: błąd zapisu: zepsuta rura”, co nie jest poprawą.
Roger Dueck,
2
@RogerDueck jest poprawny. Widzę to również w systemie Mandriva dla podobnego rodzaju problemu, który find /var/lib/mysql -xdev -type f -daystart -mmin +5 -print0 | xargs -0 ls -ldt | tail -n +1 | headniezawodnie daje xargs: ls: terminated by signal 13. Jak wiemy, problemem jest wyczerpanie danych wejściowych i tak naprawdę tylko jedno polecenie zajmuje się buforowaniem: dd. Dodanie | dd obs=1Mdo potoku naprawia SIGPIPE dla mojego przypadku użycia.
Andrew Beals,
3
Będę dalej poprawiać moją sugestię, chociaż zauważę, że nie wierzę, że xargs lub typ powinny kvetchować o SIGPIPE, do tego: type rvm | (head -1 ; dd of=/dev/null) To, oczywiście, jest podobne do innych sugestii, ponieważ powoduje przetwarzanie wszystkich danych wejściowych , ale ddpowinien być najbardziej wydajnym programem do obsługi takich rzeczy.
Andrew Beals
3
Komentarz na temat osób naruszających SIGPIPE tutaj: mail-index.netbsd.org/tech-userlevel/2013/01/07/msg007110.html
Andrew Beals
2

write error: Broken pipeWiadomość odnosi się do procesu pisania, który próbuje pisać do potoku bez pozostawionych na koniec czytania tej rury i szczególną okoliczność, że czytelników SIGPIPEsygnał ma zostać zignorowany albo przez prąd lub procesu macierzystego. Jeśli był to proces nadrzędny, który SIGPIPEzostał zignorowany, proces podrzędny nie może go cofnąć ponownie w powłoce nieinterakcyjnej.

Możliwe jest jednak zabijanie type rvmpo head -1zakończeniu przy użyciu jawnych podpowłok. W ten sposób możemy w tle type rvm, wysłać typepiddo head -1podpowłoki, a następnie zaimplementować tam pułapkę, EXITaby type rvmjawnie zabić .

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
źródło
Z odpowiedzi grawity: typedostaje wystarczająco dużo czasu, aby zauważyć nieudany zapis, przetłumaczyć kod błędu, a nawet wydrukować komunikat o błędzie do stderr, zanim zostanie zabity przez SIGPIPE . Myślę, że twoje rozwiązanie nie zapobiega reakcji producenta ( typetutaj) na nieudany zapis (z powodu zamkniętego potoku), prawda?
Piotr Dobrogost