Simpan kode keluar untuk nanti

15

Jadi saya punya skrip kecil untuk menjalankan beberapa tes.

javac *.java && java -ea Test
rm -f *.class

Sekarang masalah dengan ini adalah ketika saya menjalankan skrip ./test, itu akan mengembalikan kode keluar yang sukses bahkan jika tes gagal karena rm -f *.classberhasil.

Satu-satunya cara saya bisa memikirkan untuk melakukan apa yang saya inginkan terasa jelek bagi saya:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
if [ "$test_exit_code" != 0 ] ; then false; fi

Tapi ini sepertinya masalah umum - lakukan tugas, bersihkan, lalu kembalikan kode keluar dari tugas semula.

Apa cara paling idiomatis untuk melakukan ini (dalam bash atau hanya shell pada umumnya)?

math4tots
sumber

Jawaban:

5

Anda dapat membungkus exitdan rmmemerintahkan menjadi satu perintah sederhana dengan evalseperti:

java ... && java ...
eval "rm -f *.class; exit $?"

Dengan cara itu $?nilai 's ketika melewati exityaitu apa pun akan ditugaskan segera sebelum evalberjalan.

mikeserv
sumber
evalselalu menjadi favorit penggemar.
mikeserv
23

Saya akan pergi dengan:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
exit "$test_exit_code"

Mengapa melompat-lompat ketika exittersedia?


Anda bisa menggunakan trap:

trap 'last_error_code=$?' ERR

Sebagai contoh:

$ trap 'last_error_code=$?' ERR
$ false
$ echo $?
1
$ echo $last_error_code $?
1 0
muru
sumber
Ah saya setuju itu lebih baik daripada yang asli saya. Tetapi masih terasa tidak memuaskan bahwa saya harus secara eksplisit menyimpan kode keluar ke variabel. Apakah tidak ada cara untuk 'mendorong' kode keluar dan 'pop' lagi nanti?
math4tots
@ math4tots Coba pembaruan.
muru
Jadi dengan pembaruan Anda, saya harus menginisialisasi last_error_code ke nol dan kemudian kembali di akhir jadi saya akan memiliki kode keluar non-nol jika ada perintah yang melemparkan kesalahan? Ini adalah trik yang keren, tetapi untuk skrip hack dua baris saya, saya rasa saya lebih suka jawaban @mikeserv.
math4tots
@ math4tots Anda selalu bisa melakukannya exit ${last_error_code:=0}.
muru
@avip untuk apa? Itu sudah dalam tanda kutip tunggal, jadi variabel hanya dievaluasi ketika perangkap dipanggil.
muru
9

Sejauh yang saya tahu hal terdekat bash harus try...finallyblok dari bahasa pemrograman yang lebih seperti C (yang mungkin Anda inginkan jika tersedia) adalah trapkonstruksi, yang bekerja seperti ini:

trap "rm -f *.class" EXIT
javac *.java && java -ea Test

Ini akan menjalankan "rm -f * .class" saat skrip Anda keluar. Jika Anda memiliki sesuatu yang lebih rumit untuk dilakukan, Anda dapat menjalankannya:

cleanup() {
    ...
}
trap cleanup EXIT
javac *.java && java -ea Test

Jika Anda cenderung, Anda dapat mengubah ini menjadi idiom yang cukup umum yang bekerja kira-kira seperti try...catch...finallyblok dalam C. Sesuatu seperti ini:

(
  trap "catch_block; exit" ERR
  trap finally_block EXIT
  # contents of try goes here
)

Perhatikan bahwa tanda kurung membatasi subkulit; dengan konstruksi ini, hanya subkulit yang keluar jika perintah gagal, bukan keseluruhan skrip. Ingatlah bahwa subkulit agak mahal secara komputasi jadi jangan gunakan terlalu banyak (ratusan) di antaranya. Bergantung pada skrip Anda, Anda mungkin dapat mencapai efek yang sama lebih efisien dengan fungsi-fungsi shell dan trap ... RETURN, tetapi itu tergantung pada Anda untuk menyelidikinya.

David Z
sumber