Sortie de tuyauterie à partir d'un programme de défaillance

13

J'ai un script qui appelle un programme (en particulier, une ttf2afmpartie de tetex 3.0) qui se sépare parfois et parfois pas. Les informations dont j'ai besoin sont toujours imprimées avant qu'elles ne se séparent, mais j'ai du mal à empêcher la redirection du tuyau d'échouer et à ne rien envoyer au tuyau lorsque le programme échoue.

J'ai essayé de rediriger via un FIFO, entre parenthèses le processus avec un trueà la fin, en exécutant à partir d'une fonction shell et en encapsulant sh -c, mais le script ne semble jamais laisser le processus produire quoi que ce soit , redirigé ou autre - pas même vers stderr.

Je sais qu'il est capable de sortie, étant donné qu'il est parfaitement capable de le donner à partir de la ligne de commande, mais pas à partir d'un script pour une raison quelconque.

Ma question est, existe-t-il un moyen pour le script d'ignorer le fait que le programme segfaults et me donne quand même la sortie?

J'utilise BASH 4.1.10 (2) -release.

amphétamachine
la source

Réponses:

12

Les programmes tamponnent généralement leur sortie pour l'efficacité. Autrement dit, ils accumulent la sortie dans une zone de mémoire (appelée tampon), et ils ne sortent la sortie que lorsque le tampon est plein ou à certains points clés du programme. Lorsque le programme se termine normalement, il vide le tampon de sortie (c'est-à-dire qu'il imprime toutes les données qui y restent). Lorsqu'il se sépare, le contenu du tampon est perdu.

Vous n'observez pas cet effet lorsque vous exécutez le programme directement dans un terminal, car le comportement est différent lorsque la sortie du programme est connectée à un terminal (par opposition à un fichier normal ou à un canal). Dans un terminal, le comportement par défaut consiste à vider le tampon à la fin de chaque ligne. Par conséquent, vous verrez chaque ligne complète produite jusqu'au moment où le programme se sépare.

Vous pouvez forcer le programme à s'exécuter dans un terminal et collecter sa sortie. La façon la plus simple est de courir script. Il y a un certain nombre de désagréments que vous devrez contourner:

  • script ajoute une ligne d'en-tête au fichier de transcription, que vous devrez supprimer par la suite.
  • script ne renvoie pas le code d'état de la commande, vous devrez donc l'enregistrer quelque part si vous souhaitez connaître le défaut de segmentation ou toute autre erreur.
  • scriptentraînera une sortie normale et une erreur; vous feriez mieux d'enregistrer la sortie d'erreur dans un fichier séparé.
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- arrête d'être méchant'
la source
N'y a-t-il pas une solution plus élégante, comme un paramètre shell qui mettra le tampon de sortie à 0 ou quelque chose?
amphetamachine
4

Je l'ai finalement compris à travers un processus d'essais et d'erreurs. La solution est un peu compliquée:

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

Apparemment, les execcauses ttf2afmde prendre en charge le processus de sous-shell avec l'erreur piégée, le faisant fonctionner dans un environnement où il n'a pas d'importance s'il se trompe.

Le piégeage du ERRsignal tout compris arrêtera la mort du sous-shell et enverra un signal au script principal - qui se terminera immédiatement si c'est le cas - lorsque le programme échoue.

Le seul problème est que le noyau lui - même produira un tas de déchets de trace de pile directement sur le périphérique de la console une fois le processus segfaults, donc il n'y a aucun moyen de l'empêcher d'être sorti [à ma connaissance], mais cela n'a pas d'importance car cela n'affecte pas stdout ou stderr.

amphétamachine
la source
3
Je suis content si cela fonctionne pour vous, mais je peux affirmer avec confiance que la raison pour laquelle cela fonctionne n'est pas parce que bash définit la taille du tampon de sortie sur 0. Bash ne peut pas influencer ttf2afmdirectement la mise en mémoire tampon utilisée par . Je me demande comment (trap true ERR; exec ttf2afm "$FONT")| …parvient à se comporter différemment de ttf2afm "$FONT" | ….
Gilles 'SO- arrête d'être méchant'