Praktik terbaik untuk menggunakan $? di bash?

10

Ketika saya membaca jawaban ini tentang $? pertanyaan lain muncul di benak saya.

Apakah ada praktik terbaik untuk cara menggunakan $? di bash?


Mari kita punya contoh:

Kami memiliki skrip linier dan saya ingin tahu bahwa semua perintah dieksekusi ok. Apakah Anda pikir tidak masalah untuk memanggil fungsi kecil (sebut saja "did_it_work"), untuk memeriksa kode kesalahan dan pecahkan jika tidak.

#!/bin/bash 

function did_it_work {
    code=$1
    if [ "$code" -ne "0" ]
    then
        echo "Error failure: code $code "
        exit 1
    fi
}

dir=some/path

mkdir -p $dir
did_it_work $? 

cd $dir
did_it_work $? 

run_some_command
did_it_work $? 

Pendekatan ini tentu saja berarti bahwa saya harus secara manual menyelesaikan masalah jika ada dan jalankan kembali skrip.

Apakah Anda pikir ini ide yang bagus atau ada praktik terbaik lain untuk melakukan ini?

/Terima kasih

Johan
sumber

Jawaban:

15

Salah satu cara yang umum adalah:

die() {
    IFS=' ' # make sure "$*" is joined with spaces

    # output the arguments if any on stderr:
    [ "$#" -eq 0 ] || printf '%s\n' "$*" 1>&2
    exit 1
}

maka Anda menggunakannya seperti ini:

mkdir -p some/path || die "mkdir failed with status $?"

Atau jika Anda ingin menyertakan status keluar, Anda dapat mengubahnya ke:

die() {
    last_exit_status=$?
    IFS=' '
    printf '%s\n' "FATAL ERROR: $* (status $last_exit_status)" 1>&2
    exit 1
}

dan kemudian menggunakannya sedikit lebih mudah:

mkdir -p some/path || die "mkdir failed"

Ketika gagal, mkdirkemungkinan akan sudah mengeluarkan pesan kesalahan, sehingga yang kedua mungkin dianggap berlebihan, dan Anda bisa melakukannya:

mkdir -p some/path || exit   # with the same (failing) exit status as mkdir's
mkdir -p some/path || exit 1 # with exit status 1 always

(atau gunakan varian pertama di dieatas tanpa argumen)

Kalau-kalau Anda belum melihat command1 || command2sebelumnya, itu berjalan command1, dan jika command1gagal, itu berjalan command2.

Jadi Anda bisa membacanya seperti "membuat direktori atau mati".

Contoh Anda akan terlihat seperti:

mkdir -p some/path || die "mkdir failed"
cd some/path || die "cd failed"
some_command || die "some_command failed"

Atau Anda dapat menyelaraskan dieslebih jauh di sebelah kanan sehingga kode utama lebih jelas.

mkdir -p some/path         || die "mkdir failed"
cd some/path               || die "cd failed"
some_command               || die "some_command failed"

Atau pada baris berikut saat baris perintah panjang:

mkdir -p some/path ||
  die "mkdir failed"

cd some/path ||
  die "cd failed"

some_command ||
  die "some_command failed"

Juga, jika Anda akan menggunakan nama some/pathbeberapa kali, simpan dalam variabel sehingga Anda tidak harus terus mengetiknya, dan dapat dengan mudah mengubahnya jika perlu. Dan ketika meneruskan argumen variabel ke perintah, pastikan untuk menggunakan --pembatas opsi sehingga argumen tidak diambil sebagai opsi jika dimulai dengan -.

dir=some/path
mkdir -p -- "$dir"         || die "Cannot make $dir"
cd -P -- "$dir"            || die "Cannot cd to $dir"
some_command               || die "Cannot run some_command"
Mikel
sumber
9

Anda dapat menulis ulang kode Anda seperti ini:

#!/bin/bash
function try {
    "$@"
    code=$?
    if [ $code -ne 0 ]
    then
        echo "$1 did not work: exit status $code"
        exit 1
    fi
}

try mkdir -p some/path
try cd some/path
try run_some_command

Jika Anda sebenarnya tidak perlu mencatat kode kesalahan, tetapi hanya apakah perintahnya berhasil atau tidak, Anda dapat mempersingkat try()lebih lanjut seperti ini:

function try {
    if ! "$@"
    then
        echo "$1 did not work"
        exit 1
    fi
}
Warren Young
sumber
Anda juga dapat menggunakan kode dalam format ini. Fungsi <pre> coba {
BillThor
Jika Anda menggunakan fungsi ini sebaris dan tidak ingin kembali dari sisi sebenarnya, Anda dapat menggantinya return $?dengan :builtin.
BillThor
8

Jika Anda benar-benar ingin exitmelakukan kesalahan dan menggunakan Bash, maka Anda juga harus mempertimbangkan set -e. Dari help set:

-e Keluar segera jika perintah keluar dengan status bukan nol.

Ini tentu saja tidak memberi Anda fleksibilitas fungsi did_it_work (), tetapi ini adalah cara mudah untuk memastikan skrip bash Anda berhenti pada kesalahan tanpa menambahkan banyak panggilan ke fungsi baru Anda.

Steven D
sumber
set -eberguna. Ada beberapa perintah yang mengembalikan non-nol dalam keadaan normal (misalnya, diff). Ketika saya menggunakan set -e dalam skrip di mana saya mengharapkan pengembalian bukan nol, saya lakukan command || true.
Shawn J. Goff
2
Selain itu, bahkan jika Anda menggunakan set -e, Anda dapat mengatur "pengendali pengecualian" untuk menangkap semua kesalahan trap did_it_work EXIT.
Gilles 'SANGAT berhenti menjadi jahat'
1
Ini adalah bagian dari POSIX, bukan fitur spesifik bash. Gunakan itu tetapi waspadai beberapa jebakan mywiki.wooledge.org/BashFAQ/105
kmkaplan
@ ShawnJ.Goff Saya lebih suka melakukannya command && true. Seperti itu nilai kembali tidak berubah.
kmkaplan
@kmkaplan Komentar terakhir Anda tidak masuk akal bagi saya. Tujuannya command || trueadalah untuk mencegah set -eskrip keluar jika commandmengembalikan kode keluar yang tidak nol. Itu mengubah kode keluar karena kita perlu. Satu-satunya hal command && trueyang dijalankan adalah true(mengembalikan kode keluar nol) jika `perintah berhasil (mengembalikan kode keluar nol) - itu adalah no-op lengkap.
tripleee