Saída de tubulação de um programa de segfaulting

13

Eu tenho um script que chama um programa (especificamente ttf2afm, parte do tetex 3.0) que às vezes segfaults e às vezes não. As informações de que eu preciso são sempre impressas antes de serem segmentadas, mas estou tendo dificuldades para impedir que o redirecionamento do canal falhe e não produza nada para o canal quando o programa falha.

Tentei redirecionar por meio de um FIFO, parênteses o processo com a trueno final, executando a partir de uma função shell e inserindo-o sh -c, mas o script nunca parece permitir que o processo produza algo , redirecionado ou não - nem mesmo para o stderr.

Eu sei que é capaz de produzir, sendo perfeitamente capaz de fornecê-lo a partir da linha de comando, mas não de um script por algum motivo.

Minha pergunta é: existe alguma maneira de o script ignorar o fato de que o programa se segfaults e me fornecer a saída de qualquer maneira?

Estou executando o lançamento do BASH 4.1.10 (2).

anfetamaquina
fonte

Respostas:

12

Os programas normalmente armazenam sua saída para obter eficiência. Ou seja, eles acumulam saída em uma área de memória (chamada de buffer) e na verdade só saem quando o buffer está cheio ou em determinados pontos-chave do programa. Quando o programa termina normalmente, limpa o buffer de saída (ou seja, imprime todos os dados que restam nele). Quando é segmentado, o conteúdo do buffer é perdido.

Você não observa esse efeito ao executar o programa diretamente em um terminal, porque o comportamento é diferente quando a saída do programa é conectada a um terminal (ao contrário de um arquivo comum ou um pipe). Em um terminal, o comportamento padrão é liberar o buffer no final de cada linha. Portanto, você verá todas as linhas completas produzidas até o momento em que o programa for segmentado.

Você pode forçar o programa a executar em um terminal e coletar sua saída. A maneira mais simples é correr script. Há uma série de aborrecimentos que você precisará solucionar:

  • script adiciona uma linha de cabeçalho ao arquivo de transcrição, que você precisará remover posteriormente.
  • script não retorna o código de status do comando, portanto, você precisará salvá-lo em algum lugar se quiser saber sobre o segfault ou qualquer outro erro.
  • scriptcausará saída normal e erro de saída; é melhor salvar a saída de erro em um arquivo separado.
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- parar de ser mau'
fonte
Não existe uma solução mais elegante, como algumas configurações de shell que definirão o buffer de saída como 0 ou algo assim?
Amphetamachine
4

Finalmente descobri isso através de um processo de tentativa e erro. A solução é meio complicada:

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

Aparentemente, as execcausas ttf2afmassumem o processo do subshell com o erro interceptado, fazendo com que ele opere em um ambiente em que não importa se ele é falhado.

Interceptar o ERRsinal com tudo incluído impedirá que o subshell morra e envie um sinal para o script principal - que terminará imediatamente se o fizer - quando o programa falhar.

O único problema é que o próprio kernel produzirá um monte de lixo de rastreamento de pilha diretamente no dispositivo do console, uma vez que o processo segfaults, portanto não há como impedir que ele seja produzido [que eu saiba], mas isso não importa pois não afeta stdout ou stderr.

anfetamaquina
fonte
3
Fico feliz que isso funcione para você, mas posso afirmar com confiança que o motivo pelo qual ele funciona não é porque o bash está configurando o tamanho do buffer de saída como 0. O bash não pode influenciar o buffer usado ttf2afmdiretamente. Eu me pergunto como (trap true ERR; exec ttf2afm "$FONT")| …consegue se comportar de maneira diferente ttf2afm "$FONT" | ….
Gilles 'SO- stop be evil'