Bagaimana cara keluar dari skrip shell jika salah satu bagian gagal?

51

Bagaimana saya bisa menulis skrip shell yang keluar, jika salah satu bagian gagal? Misalnya, jika cuplikan kode berikut gagal, maka skrip harus keluar.

n=0
until [ $n -ge 5 ]
do
  gksu *command* && break
  n=$[$n+1]
  sleep 3
Weylyn
sumber

Jawaban:

88

Salah satu pendekatan adalah menambahkan set -eke awal skrip Anda. Itu berarti (dari help set):

  -e  Exit immediately if a command exits with a non-zero status.

Jadi jika salah satu perintah Anda gagal, skrip akan keluar.

Atau, Anda dapat menambahkan exitpernyataan eksplisit di titik kegagalan yang memungkinkan:

command || exit 1
terdon
sumber
5
Saya (dan bash wiki ) lebih suka orang menaruh pemikiran dalam penanganan kesalahan yang tepat daripada menggunakan fitur (rusak oleh desain IMO) set -e. Itu tidak benar-benar berlaku di sini. OP ingin keluar dari skrip setelah 5 upaya gagal menjalankan perintah.
Stéphane Chazelas
1
@ StéphaneChazelas Saya tidak akan berdebat dengan Anda tentang apakah itu rusak, saya yakin Anda benar. Namun, OP bertanya "Bagaimana saya bisa menulis skrip shell yang keluar, jika salah satu bagiannya gagal?", Apa yang membuat Anda berpikir tentang keluar setelah 5 kegagalan?
terdon
karena saya tidak bisa memikirkan cara lain pertanyaan itu dapat ditafsirkan.
Stéphane Chazelas
1
@ StéphaneChazelas Anda mungkin benar. Saya menafsirkannya secara harfiah: bagaimana seluruh skrip bisa keluar jika ada bagian yang gagal. Dan set -esatu-satunya cara saya tahu untuk melakukan itu.
terdon
Dalam snipet skrip itu, perintah yang dapat memicu set -eadalah sleep( breakmenjadi builtin khusus akan menyebabkan skrip keluar pada kegagalan di sebagian besar shell, perintah di ifatau di sebelah kiri &&tidak terpengaruh oleh set -e, n=...bisa gagal jika nhanya baca, tapi kemudian yang akan keluar dari skrip tanpa set -ejuga), sehingga interpretasi terdengar sangat tidak mungkin. Saya setuju pertanyaan itu tidak ditulis dengan baik.
Stéphane Chazelas
17

Anda dapat keluar dari skrip di mana saja menggunakan kata kunci exit. Anda juga dapat menentukan kode keluar untuk menunjukkan kepada program lain bahwa atau bagaimana skrip Anda gagal, misalnya exit 1atau exit 2lain - lain (dengan konvensi, kode keluar 0 untuk kesuksesan dan apa pun yang lebih besar dari 0 menandakan kegagalan; namun, juga dengan konvensi, keluar kode di atas 127 dicadangkan untuk pemutusan yang tidak normal (misalnya dengan sinyal)).

Konstruksi generik untuk keluar dari kegagalan adalah

if [ failure condition ]; then
    exit n
fi

dengan cocok failure conditiondan n. Tetapi dalam skenario tertentu Anda dapat melanjutkan berbeda. Sekarang untuk kasus Anda, saya menafsirkan pertanyaan Anda bahwa jika salah satu dari lima doa gksugagal, maka Anda bermaksud untuk keluar. Salah satu caranya adalah dengan menggunakan fungsi seperti ini

function try_command {
    for i in 1 2 3 4 5 ; do
        if gksu command ; then
            return 0
        fi
    fi
    exit 1
}

dan kemudian, aktifkan loop oleh try_command.

Ada (lebih) cara canggih atau canggih untuk menjawab pertanyaan Anda. Namun, solusi di atas lebih mudah diakses oleh pemula daripada, katakanlah, solusi Stephane.

countermode
sumber
10
attempt=0
until gksu command; do
  attempt=$((attempt + 1))
  if [ "$attempt" -gt 5 ]; then
    exit 1
  fi
done

exitkeluar dari skrip kecuali jika dipanggil dalam subkulit. Jika bagian skrip tersebut berada dalam subkulit, misalnya karena berada di dalam (...)atau $(...)atau bagian dari pipa-saluran, maka itu hanya akan keluar dari subkulit itu .

Dalam hal itu, jika Anda ingin skrip keluar selain subkulit, maka Anda harus memanggil exitkeluar subkulit tersebut.

Misalnya, di sini dengan 2 tingkat subshell bersarang:

(
  life=hard
  output=$(
    echo blah
    [ "$life" = easy ] || exit 1 # exit subshell
    echo blih not run
  ) || exit # if the subshell exits with a non-zero exit status,
            # exit as well with the same exit status

  echo not run either
) || exit # if the subshell exits with a non-zero exit status,
          # exit as well with the same exit status

Ini bisa menjadi lebih sulit jika subshell adalah bagian dari pipa. bashmemiliki khusus $PIPESTATUSarray, mirip dengan zsh's $pipestatussalah satu yang dapat membantu Anda di sini:

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi
Stéphane Chazelas
sumber
3

Trap akan melakukan tindakan setelah menerima sinyal.

trap "echo EXIT;  exit" 0
trap "echo HUP;   exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT;  exit" 3
trap "echo ERR;   exit" ERR
n=0
until [ $n -ge 5 ]
do
  n=$[$n+1]
  echo $n
  sleep 3
done

Jalankan ini dan biarkan keluar secara normal. Itu terperangkap pada sinyal 0.

EXIT

Jalankan lagi dan interupsi dengan ^ C. Ia terperangkap pada sinyal 2 dan sinyal 0.

CTL-C
EXIT

Status keluar yang tidak nol akan menjebak ERR

ERR
EXIT
RobP
sumber