Przesyłanie danych wyjściowych z programu segfault

13

Mam skrypt, który wywołuje program (w szczególności ttf2afmczęść tetex 3.0), który czasami segfuje, a czasem nie. Informacje, których potrzebuję, są zawsze drukowane, zanim ulegną segregacji, ale trudno mi jest powstrzymać przekierowanie potoku przed awarią i nie wysyłać niczego do potoku, gdy program zawiedzie.

Próbowałem przekierować przez FIFO, nawiasować proces za pomocą trueznaku na końcu, wykonywać z funkcji powłoki i otaczać sh -c, ale skrypt nigdy nie wydaje, aby proces wypuścił cokolwiek , przekierował lub w inny sposób - nawet do stderr.

Wiem, że jest w stanie generować dane wyjściowe, ponieważ jest doskonale zdolny do podania go z wiersza poleceń, ale z jakiegoś powodu nie ze skryptu.

Moje pytanie brzmi: czy jest jakikolwiek sposób, aby skrypt zignorował fakt, że program się nie zgadza i daje mi wynik?

Używam wersji BASH 4.1.10 (2).

amfetamachina
źródło

Odpowiedzi:

12

Programy zwykle buforują swoje wyniki w celu zwiększenia wydajności. Oznacza to, że gromadzą dane wyjściowe w obszarze pamięci (zwanym buforem) i faktycznie uzyskują dane wyjściowe tylko wtedy, gdy bufor jest pełny lub w niektórych kluczowych punktach programu. Kiedy program kończy się normalnie, opróżnia bufor wyjściowy (tzn. Drukuje wszelkie dane, które w nim pozostały). Gdy się segreguje, zawartość bufora zostaje utracona.

Nie obserwuje się tego efektu podczas uruchamiania programu bezpośrednio w terminalu, ponieważ zachowanie jest inne, gdy wyjście programu jest podłączone do terminala (w przeciwieństwie do zwykłego pliku lub potoku). W terminalu domyślnym zachowaniem jest opróżnianie bufora na końcu każdej linii. Dlatego zobaczysz każdą kompletną linię, która zostanie utworzona do momentu, gdy program ulegnie awarii.

Możesz wymusić uruchomienie programu w terminalu i zebranie jego danych wyjściowych. Najprostszym sposobem jest uruchomienie script. Istnieje wiele niedogodności, które trzeba obejść:

  • script dodaje wiersz nagłówka do pliku transkrypcji, który trzeba będzie później usunąć.
  • script nie zwraca kodu statusu polecenia, więc musisz go gdzieś zapisać, jeśli chcesz wiedzieć o segfault lub innym błędzie.
  • scriptspowoduje normalne wyjście i błąd; lepiej zapisać wynik błędu w osobnym pliku.
export FONT="foo"
script -q -c '
    ttf2afm "$FONT.ttf" 2>"$FONT.ttf2afm-err";
    echo $? >"$FONT.ttf2afm-status"
' "$FONT.ttf2afm-typescript"
tail -n +2 <"$FONT.ttf2afm-typescript" >"foo.afm"
rm "$FONT.ttf2afm-typescript"
if [ "$(cat "$FONT.ttf2afm-status")" -ne 0 ]; then
  echo 1>&2 "Warning: ttf2afm failed"
  cat "$FONT.ttf2afm-err"
fi
Gilles „SO- przestań być zły”
źródło
Czy nie ma bardziej eleganckiego rozwiązania, takiego jak ustawienie powłoki, które ustawi bufor wyjściowy na 0 lub coś takiego?
amfetamachina
4

W końcu doszedłem do tego przez proces prób i błędów. Rozwiązanie jest dość skomplikowane:

(trap 'true' ERR; exec ttf2afm "$FONT") |
grep ...

Najwyraźniej execprzyczyny ttf2afmprzejmują proces podpowłoki z błędem uwięzionym, powodując, że działa on w środowisku, w którym nie ma znaczenia, czy ulegnie awarii.

Wychwytywanie ERRsygnału obejmującego wszystko zatrzyma śmierć powłoki wewnętrznej i wyśle ​​sygnał do skryptu głównego - który natychmiast się zakończy, jeśli to zrobi - gdy program zawiedzie.

Jedynym problemem jest to, że samo jądro wyśle ​​całą wiązkę śmieci śledzenia stosu bezpośrednio na urządzenie konsoli, gdy proces ulegnie segregacji, więc nie ma sposobu, aby zapobiec wyświetlaniu go [o czym wiem], ale to nie ma znaczenia ponieważ nie wpływa to na stdout ani stderr.

amfetamachina
źródło
3
Cieszę się, że to działa dla ciebie, ale mogę śmiało twierdzić, że powodem tego nie jest to, że bash ustawia rozmiar bufora wyjściowego na 0. Bash nie może wpływać bezpośrednio na buforowanie używane przez ttf2afm. Zastanawiam się, jak (trap true ERR; exec ttf2afm "$FONT")| …udaje się zachowywać inaczej niż ttf2afm "$FONT" | ….
Gilles „SO- przestań być zły”