Output pipa dari program segfaulting

13

Saya memiliki skrip yang memanggil program (khususnya ttf2afm, bagian dari tetex 3.0) yang terkadang segfaults dan terkadang tidak. Informasi yang saya butuhkan selalu dicetak sebelum segfaults, tapi saya mengalami kesulitan menghentikan pengalihan pipa gagal dan tidak menghasilkan apa pun ke pipa ketika program gagal.

Saya sudah mencoba mengarahkan ulang melalui FIFO, mengurung proses dengan truepada akhirnya, mengeksekusi dari fungsi shell dan membungkusnya sh -c, tetapi skrip sepertinya tidak pernah membiarkan proses menghasilkan apa pun , diarahkan atau sebaliknya - bahkan tidak ke stderr.

Saya tahu itu mampu menghasilkan, karena itu mampu memberikannya dari command-line, tetapi tidak dari skrip untuk beberapa alasan.

Pertanyaan saya adalah, adakah cara agar skrip mengabaikan fakta bahwa program itu segfault dan tetap memberi saya hasilnya?

Saya menjalankan BASH 4.1.10 (2) -release.

amphetamachine
sumber

Jawaban:

12

Program biasanya menyangga outputnya untuk efisiensi. Yaitu, mereka mengakumulasi output dalam area memori (disebut buffer), dan mereka benar-benar mendapatkan output hanya ketika buffer penuh atau pada titik-titik kunci tertentu dalam program. Ketika program berakhir secara normal, ia akan mengeluarkan buffer output (yaitu mencetak semua data yang tertinggal di dalamnya). Ketika segfaults, konten buffer hilang.

Anda tidak mengamati efek ini ketika menjalankan program langsung di terminal karena perilakunya berbeda ketika output program terhubung ke terminal (sebagai lawan dari file biasa atau pipa). Di terminal, perilaku default adalah menyiram buffer di akhir setiap baris. Oleh karena itu Anda akan melihat setiap baris lengkap yang dihasilkan hingga titik ketika program segfaults.

Anda dapat memaksa program untuk berjalan di terminal dan mengumpulkan hasilnya. Cara paling sederhana adalah menjalankan script. Ada beberapa gangguan yang perlu Anda atasi:

  • script menambahkan baris header ke file transkrip, yang harus Anda hapus sesudahnya.
  • script tidak mengembalikan kode status dari perintah, jadi Anda harus menyimpannya di suatu tempat jika Anda ingin tahu tentang segfault atau kesalahan lainnya.
  • scriptakan menyebabkan output dan kesalahan normal; Anda sebaiknya menyimpan output kesalahan ke file terpisah.
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 'SANGAT berhenti menjadi jahat'
sumber
Apakah tidak ada solusi yang lebih elegan, seperti beberapa pengaturan shell yang akan mengatur buffer output ke 0 atau sesuatu?
amphetamachine
4

Saya akhirnya menemukan jawabannya melalui proses coba-coba. Solusinya agak berbelit-belit:

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

Rupanya execpenyebab ttf2afmuntuk mengambil alih proses subkulit dengan kesalahan yang terperangkap, menyebabkannya beroperasi di lingkungan di mana tidak masalah jika segfaults.

Menjebak ERRsinyal all-inclusive akan menghentikan subshell dari sekarat dan mengirim sinyal ke skrip utama - yang akan segera berakhir jika itu terjadi - ketika program gagal.

Satu-satunya masalah adalah bahwa kernel itu sendiri akan menampilkan sejumlah besar tumpukan jejak sampah langsung ke perangkat konsol setelah proses segfault, jadi tidak ada cara untuk mencegahnya menjadi keluaran [yang saya tahu], tetapi itu tidak masalah karena tidak mempengaruhi stdout atau stderr.

amphetamachine
sumber
3
Saya senang jika ini bekerja untuk Anda, tetapi saya dapat dengan yakin mengklaim bahwa alasan kerjanya bukan karena bash menetapkan ukuran buffer output menjadi 0. Bash tidak dapat memengaruhi buffering yang digunakan ttf2afmsecara langsung. Saya bertanya-tanya bagaimana (trap true ERR; exec ttf2afm "$FONT")| …mengelola untuk berperilaku berbeda ttf2afm "$FONT" | ….
Gilles 'SANGAT berhenti menjadi jahat'