Bagaimana saya bisa membuat script ini keluar error berdasarkan hasil for loop?


13

Saya memiliki skrip bash yang digunakan set -o errexitsehingga saat kesalahan seluruh skrip keluar pada titik kegagalan.
Skrip menjalankan curlperintah yang terkadang gagal untuk mengambil file yang dimaksud - namun ketika ini terjadi skrip tidak keluar.

Saya telah menambahkan satu forloop ke

  1. jeda selama beberapa detik kemudian coba lagi curlperintah
  2. gunakan falsedi bagian bawah for loop untuk menentukan status keluar non-nol default - jika perintah curl berhasil - loop rusak dan status keluar dari perintah terakhir harus nol.
#! /bin/bash

set -o errexit

# ...

for (( i=1; i<5; i++ ))
do
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    if [ -f ~/.vim/autoload/pathogen.vim ]
    then
        echo "file has been retrieved by curl, so breaking now..."
        break;
    fi

    echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
    sleep 5
    # exit with non-zero status so main script will errexit
    false

done

# rest of script .....

Masalahnya adalah ketika curlperintah gagal, loop mengulangi perintah lima kali - jika semua upaya tidak berhasil maka for loop selesai dan script utama dilanjutkan - alih-alih memicu errexit.
Bagaimana saya bisa membuat seluruh skrip keluar jika curlpernyataan ini gagal?

Jawaban:


18

Menggantikan:

done

dengan:

done || exit 1

Ini akan menyebabkan kode keluar jika forloop keluar dengan kode keluar yang tidak nol.

Sebagai titik trivia, 1in exit 1tidak diperlukan. exitPerintah sederhana akan keluar dengan status keluar dari perintah yang dieksekusi terakhir yaitu false(kode = 1) jika unduhan gagal. Jika unduhan berhasil, kode keluar dari loop adalah kode keluar dari echoperintah. echobiasanya keluar dengan kode = 0, sukses secara signifikan. Dalam hal itu, ||tidak memicu dan exitperintah tidak dijalankan.

Terakhir, perhatikan bahwa set -o errexitbisa penuh kejutan. Untuk diskusi tentang pro dan kontra, lihat FAQ Greg # 105 .

Dokumentasi

Dari man bash:

untuk ((expr1; expr2; expr3)); lakukan daftar; dilakukan
Pertama, ekspresi aritmatika expr1 dievaluasi sesuai aturan yang dijelaskan di bawah ini di bawah EVALUASI ARITHMETIC. Ekspresi ekspresi aritmatika kemudian dievaluasi berulang kali hingga bernilai nol. Setiap kali expr2 dievaluasi menjadi nilai bukan nol, daftar dieksekusi dan expr3 ekspresi aritmatika dievaluasi. Jika ada ekspresi yang dihilangkan, itu berperilaku seolah-olah itu mengevaluasi ke 1. Nilai kembali adalah status keluar dari perintah terakhir dalam daftar yang dieksekusi, atau salah jika salah satu ekspresi tidak valid. [Penekanan ditambahkan]


Apakah Anda pikir itu akan menjadi ide yang baik untuk menempatkan truesebelum pernyataan break menjadi eksplisit dan memastikan nilai keluar dari loop?
RobertL

1
Saya pikir eksplisit itu lebih baik daripada implisit . Itu sebabnya saya menulis exit 1ketika sekadar exitakan berhasil. Namun, ini adalah masalah gaya dan orang lain dapat memiliki pendapat mereka sendiri.
John1024

1
bekerja dengan baik! terima kasih :) secara pribadi saya akan membaca exitsebagai pintu keluar sederhana - yang mengakhiri skrip dengan sendirinya. exit 1 akan membacakan kepada saya sebagai "sinyal" untuk beberapa proses lain (yaitu errexit) - bahwa itu harus mengakhiri skrip berdasarkan "hasil" dari exit 1. - jadi saya telah pergi dengan exittetapi terima kasih untuk penjelasannya
the_velour_fog

1
Jika skrip Anda keluar karena kondisi kesalahan, Anda harus menelepon exit 1. Itu tidak mempengaruhi errexitsama sekali. Itu hanya memberitahu program panggilan bahwa ada yang tidak beres. The falseperintah berisi satu pernyataan: exit(1). 99,9% dari perintah Unix mengembalikan 0 pada keberhasilan dan bukan nol pada kesalahan. Anda juga harus.
RobertL

2

Jika Anda telah errexitmengatur, maka falsepernyataan tersebut akan menyebabkan skrip segera keluar. Hal yang sama jika curlperintah gagal.

Contoh skrip Anda, seperti yang ditulis, harus keluar setelah curlkegagalan perintah pertama saat panggilan pertama kali falsejika errexit diatur.

Untuk melihat cara kerjanya (saya menggunakan singkatan -euntuk mengatur errexit:

$ ( set -e;  false; echo still here )
$

$ ( set +e;  false; echo still here )
still here
$

Jadi, jika curlperintah dijalankan lebih dari sekali, skrip ini tidak errexitdisetel.


1
set -elebih halus dari itu. Itu tidak akan keluar setelah perintah pertama gagal dalam satu lingkaran. Anda dapat membuktikannya sendiri dengan menjalankan (set -e; for (( i=1; i<5; i++ )); do echo $i; false; done || echo "FAIL"; )dan mencatat bahwa kode tersebut berjalan falseempat kali. Untuk informasi lebih lanjut set -e, lihat FAQ Greg # 105 .
John1024

@ John1024 Terima kasih. Yang ini turun turun dan keluar.
RobertL

@ John1024 Tapi saya kira bukti masih errexitbelum ditetapkan. Silakan terapkan logika pada skrip dalam pertanyaan. Jalankan ini: (set -e; for (( i=1; i<5; i++ )); do echo $i; false; done ; echo still here ) Ya menguji nilai kembali dengan if while || &&dll tidak memicu errexit. Script asli bukan ||untuk loop.
RobertL

Saya baru saja memperhatikan bahwa saya tidak menunjukkan set -o errexitperintah dalam kode contoh saya, telah menambahkannya sekarang - dan bagi saya itu bukan kesalahan yang keluar seperti yang diharapkan. Saya perlu menjaga falsesebagai perintah terakhir dalam for loop, kemudian tutup dengan done || exit [1]- maka itu bekerja dengan baik!
the_velour_fog

@ Robert Saya mengerti maksud Anda.
John1024

1

set -o errexit dapat menjadi rumit dalam loop dan subkulit, karena Anda harus melewati jalan keluar dari proses.

Memutuskan loop (bahkan dalam operasi normal) dianggap praktik yang buruk. Anda dapat memanggil saya old-school untuk memilih while-loop daripada for-loop untuk dua kondisi, tetapi saya merasa lebih baik membaca:

i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
    [ $i -eq 1 ] || sleep 5
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    RET=$?
    i=$((i+1))
done
exit $RET

0

Jika errexitdiatur dan curlperintah gagal, skrip berakhir tepat setelah perintah keriting yang gagal. Dalam manual bash, tidak ada petunjuk yang set -emengabaikan status pengembalian tunggal yang gagal pada perintah majemuk. Ini hanya akan menjadi kasus jika perintah gabungan dieksekusi dalam konteks di mana set -ediabaikan.
https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin

Coba contoh yang sedikit diadaptasi yang diposting oleh RobertL. Ini berhenti pada iterasi pertama tepat setelah perintah salah:

( set -e; for (( i=1; i<5; i++ )); do echo $i; false; echo "${i}. iteration done"; done ; echo "loop done" )

0

Anda cukup menambahkan opsi --fail ke perintah curl, ini akan menyelesaikan masalah Anda, skrip akan gagal dan keluar dari kesalahan jika perintah curl gagal, jika sangat berguna juga saat menggunakan curl di jenkins pipeline:

curl -LSso --fail ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.