Menggunakan kode "khusus" untuk keluar dari skrip shell

15

Baru-baru ini saya menemukan daftar Kode Keluar Dengan Makna Khusus dari Panduan Script Bash Lanjutan. Mereka menyebut kode-kode ini sebagai cadangan dan merekomendasikan bahwa:

Menurut tabel di atas, kode keluar 1-2, 126-165, dan 255 memiliki makna khusus, dan karenanya harus dihindari untuk parameter keluar yang ditentukan pengguna.

Beberapa waktu yang lalu, saya menulis skrip yang menggunakan kode status keluar berikut:

  • 0 - sukses
  • 1 - nama host salah
  • 2 - argumen yang tidak valid ditentukan
  • 3 - hak pengguna tidak mencukupi

Ketika saya menulis skrip saya tidak mengetahui adanya kode keluar khusus jadi saya mulai saja pada 1 untuk kondisi kesalahan pertama, dan menambah status keluar untuk setiap jenis kesalahan berturut-turut.

Saya menulis skrip dengan maksud bahwa pada tahap selanjutnya dapat dipanggil oleh skrip lain (yang dapat memeriksa kode keluar yang tidak nol). Saya belum benar-benar melakukannya; sejauh ini saya hanya menjalankan skrip dari shell interaktif saya (Bash) dan saya bertanya-tanya apa / jika ada masalah yang disebabkan oleh penggunaan kode keluar khusus saya. Seberapa relevan / penting rekomendasi dari Panduan Advanced Bash-Scripting?

Saya tidak dapat menemukan saran yang menguatkan dalam dokumentasi Bash; bagiannya pada Status Keluar hanya mencantumkan kode keluar yang digunakan oleh Bash tetapi tidak menyatakan bahwa semua ini dicadangkan atau diperingatkan agar tidak menggunakannya untuk skrip / program Anda sendiri.

Anthony G - keadilan untuk Monica
sumber
6
Saya, dan lainnya, menganggap ABSG umumnya berkualitas rendah. Menurut pendapat saya, penulis halaman yang Anda tautkan membuat pernyataan yang tidak didukung bahwa kode keluar yang terdaftar disediakan berdasarkan, tampaknya, pada kenyataan bahwa shell itu sendiri menggunakannya untuk makna tertentu. Ada upaya untuk membuat standar untuk skrip, tidak ada yang berhasil. Yang penting adalah mendokumentasikan kode kesalahan yang Anda pilih sehingga konsumen skrip Anda (misalnya skrip lain) tahu apa yang harus dilakukan berdasarkan pada kode tersebut.
Dijeda sampai pemberitahuan lebih lanjut.
@DennisWilliamson Jika Anda memposting komentar Anda sebagai jawaban, saya akan senang untuk mengangkatnya; Saya sudah memilih semua jawaban lain karena saya menemukan masing-masing dari mereka berguna. Sementara jawaban Anda serupa dalam isi dengan David King (dan pada tingkat yang lebih rendah dari Zwol), Anda menyatakan secara eksplisit bahwa tidak ada bukti untuk pernyataan dalam kutipan ABSG.
Anthony G - keadilan untuk Monica
1
Terima kasih atas tawarannya, tetapi saya yakin komentar saya harus tetap seperti itu.
Dijeda sampai pemberitahuan lebih lanjut.
Sejak itu saya menemukan bahwa spesifikasi POSIX mencakup saran serupa sehingga saya telah menambahkan informasi itu ke jawaban saya sendiri (berisi hasil penelitian saya sejak mengajukan pertanyaan ini).
Anthony G - keadilan untuk Monica

Jawaban:

10

Ada beberapa upaya untuk menstandarkan arti dari kode keluar proses. Selain yang Anda sebutkan, saya tahu:

  • BSD memiliki sysexits.hyang mendefinisikan makna untuk nilai dari 64 ke atas.

  • grepDokumen GNU yang keluar kode 0 berarti setidaknya satu kecocokan ditemukan, 1 berarti tidak ada kecocokan yang ditemukan, dan 2 berarti kesalahan I / O terjadi; konvensi ini jelas juga berguna untuk program lain yang perbedaan antara "tidak ada yang salah tetapi saya tidak menemukan apa pun" dan "kesalahan I / O terjadi" adalah bermakna.

  • Banyak implementasi fungsi pustaka C systemmenggunakan kode keluar 127 untuk menunjukkan program tidak ada atau gagal untuk memulai.

  • Pada Windows, NTSTATUSkode (yang tersebar secara tidak nyaman di seluruh ruang angka 32-bit) dapat digunakan sebagai kode keluar, terutama kode yang mengindikasikan proses dihentikan karena kelakuan buruk bencana (mis STATUS_STACK_OVERFLOW.).

Anda tidak dapat mengandalkan program apa pun yang mematuhi salah satu dari konvensi ini. Satu-satunya aturan yang dapat diandalkan adalah bahwa kode keluar 0 berhasil dan yang lainnya adalah semacam kegagalan. (Perhatikan bahwa C89 ini EXIT_SUCCESSadalah tidak dijamin memiliki nilai nol, namun exit(0)diperlukan untuk berperilaku identik dengan exit(EXIT_SUCCESS). Bahkan jika nilai-nilai tidak sama)

zwol
sumber
Terima kasih. Sulit untuk memilih satu jawaban daripada yang lain, tetapi saya menerima yang ini karena menjawab pertanyaan saya sementara juga memberikan rasa yang luas dari berbagai kode keluar yang digunakan (dengan tautan yang relevan): lebih pantas daripada 3 upvotes itu saat ini telah.
Anthony G - keadilan untuk Monica
11

Tidak ada kode keluar yang memiliki arti khusus, tetapi nilai dalam $?mungkin memiliki arti khusus.

Cara Bourne Shell dan ksh93 menangani dan meneruskan kode keluar dan situasi kesalahan ke variabel shell $?adalah masalahnya. Bertentangan dengan apa yang Anda daftarkan, hanya nilai-nilai berikut untuk yang $?memiliki arti khusus:

  • 126 Tidak dapat menjalankan biner meskipun ada
  • 127 Biner yang ditentukan tidak ada
  • 128 status keluar adalah == 0 tetapi ada beberapa masalah yang tidak ditentukan

Selain itu, ada shell yang tidak ditentukan dan rentang $?kode khusus platform > 128 yang dicadangkan untuk program yang terganggu oleh sinyal:

  • Bourne Shell bash dan ksh88 menggunakan 128 + nomor sinyal
  • ksh93 menggunakan nomor sinyal 256+.

Nilai-nilai lain tidak memberikan masalah karena mereka dapat dibedakan dari nilai- $?nilai khusus shell .

Secara khusus, nilai 1 dan 2 tidak digunakan untuk kondisi khusus tetapi hanya kode keluar yang digunakan oleh perintah builtin yang dapat bertindak sama ketika mereka tidak ada builtin. Jadi sepertinya penunjuk ke panduan skrip bash yang Anda berikan bukan manual yang baik karena hanya mencantumkan kode yang digunakan oleh bash tanpa berkomentar apakah kode tertentu adalah nilai khusus yang harus dihindari untuk skrip sendiri.

Versi yang lebih baru dari Bourne Shell menggunakan waitid()alih-alih waitpid()menunggu program untuk keluar dan waitid()(diperkenalkan 1989 untuk SVr4) menggunakan antarmuka syscall yang lebih baik (mirip dengan apa yang sudah digunakan UNOS pada 1980).

Ketika versi Bourne Shell yang lebih baru menyandikan alasan keluar dalam variabel terpisah ${.sh.code}/ ${.sh.codename}daripada kode keluar yang ada di ${.sh.status}/ ${.sh.termsig}, lihat http://schillix.sourceforge.net/man/man1/bosh.1.html , kode keluar tidak kelebihan beban dengan status khusus, dan, sebagai akibat dari menggunakan `waitid (), Bourne Shell sekarang mendukung pengembalian semua 32 bit dari kode keluar - bukan hanya bit 8 yang rendah.

BTW: berhati-hatilah untuk tidak melakukan exit(256)atau mirip dari C-program atau skrip shell, karena ini menghasilkan $?ditafsirkan sebagai 0 di shell klasik.

schily
sumber
2
BTW: Saya membuat laporan bug terhadap FreeBSD dan kernel Linux untuk waitid()bug ini sekitar akhir Mei. Orang-orang FreeBSD memperbaiki masalah dalam waktu 20 jam, orang-orang Linux tidak tertarik untuk memperbaiki bug mereka. ... dan orang-orang Cygwin mengatakan bahwa mereka bug oleh bug yang kompatibel dengan Linux ;-)
schily
2
Perilaku ini diperlukan oleh Spesifikasi Unix Tunggal. Ada nilai 32-bit, ya, tetapi nilai itu berisi bitfield 8-bit yang mengandung nilai bit 8-bit rendah _exit. Harap tautkan laporan bug FreeBSD yang Anda maksud, mungkin saya salah memahami masalah yang Anda jelaskan.
Random832
2
OP menandai pertanyaan dengan bash dan menyebut Bash dalam teks pertanyaan. Bash adalah shell yang diturunkan dari Bourne. Itu tidak mendukung ${.sh.}variabel. Memang benar, bahwa Anda mengatakan "Bourne" dan bukan "Bourne-turunan" (meskipun Anda memasukkan ksh93).
Dijeda sampai pemberitahuan lebih lanjut.
2
Jawaban ini tampaknya sangat spesifik untuk varian khusus Anda dari beberapa Unix yang diturunkan dari SVR4. Harap lebih jelas tentang apa yang portabel dan apa yang tidak, ingat bahwa tidak ada yang namanya "Bourne shell," kecuali Anda maksudkan yang ada di V7.
zwol
4
Sebaliknya, saya percaya Andalah yang meremehkan kisaran variasi di sini, terutama variasi sejarah. Anda membuatnya terdengar seperti /bin/shdapat diandalkan untuk berperilaku secara konsisten menggunakan kode keluar lintas-platform khusus ini, yang tidak benar. (Saya tidak peduli apakah sistem tertentu /bin/shdapat dikatakan sebagai "shell Bourne nyata". Jauh lebih penting untuk mengetahui bahwa semua ini tidak ada dalam POSIX, dan bahwa sebagian besar hal yang Anda kutip sebagai "sistem Unix nyata" tidak t tetap menyediakan POSIX-compliant /bin/sh.)
zwol
6

Untuk skrip shell, saya kadang-kadang in-source setara sysexist.hshell dengan kode keluar shell-dilindungi (diawali dengan S_EX_), yang saya beri namaexit.sh

Itu pada dasarnya:

EX_OK=0 # successful termination 
EX__BASE=64     # base value for error messages 
EX_USAGE=64     # command line usage error 
EX_DATAERR=65   # data format error 
EX_NOINPUT=66   # cannot open input 
EX_NOUSER=67    # addressee unknown 
EX_NOHOST=68    # host name unknown 
EX_UNAVAILABLE=69       # service unavailable 
EX_SOFTWARE=70  # internal software error 
EX_OSERR=71     # system error (e.g., can't fork) 
EX_OSFILE=72    # critical OS file missing 
EX_CANTCREAT=73 # can't create (user) output file 
EX_IOERR=74     # input/output error 
EX_TEMPFAIL=75  # temp failure; user is invited to retry 
EX_PROTOCOL=76  # remote error in protocol 
EX_NOPERM=77    # permission denied 
EX_CONFIG=78    # configuration error 
EX__MAX=78      # maximum listed value 

#System errors
S_EX_ANY=1      #Catchall for general errors
S_EX_SH=2       #Misuse of shell builtins (according to Bash documentation); seldom seen
S_EX_EXEC=126   #Command invoked cannot execute         Permission problem or command is not an executable
S_EX_NOENT=127  #"command not found"    illegal_command Possible problem with $PATH or a typo
S_EX_INVAL=128  #Invalid argument to exit       exit 3.14159    exit takes only integer args in the range 0 - 255 (see first footnote)                                                                                        
#128+n  Fatal error signal "n"  kill -9 $PPID of script $? returns 137 (128 + 9)                               
#255*   Exit status out of range        exit -1 exit takes only integer args in the range 0 - 255              
S_EX_HUP=129                                                                                                   
S_EX_INT=130   
#...

Dan dapat dihasilkan dengan:

#!/bin/sh
src=/usr/include/sysexits.h
echo "# Generated from \"$src\"" 
echo "# Please inspect the source file for more detailed descriptions"
echo
< "$src" sed -rn 's/^#define  *(\w+)\s*(\d*)/\1=\2/p'| sed 's:/\*:#:; s:\*/::'
cat<<'EOF'

#System errors
S_EX_ANY=1  #Catchall for general errors
S_EX_SH=2   #Misuse of shell builtins (according to Bash documentation); seldom seen
S_EX_EXEC=126   #Command invoked cannot execute     Permission problem or command is not an executable
S_EX_NOENT=127  #"command not found"    illegal_command Possible problem with $PATH or a typo
S_EX_INVAL=128  #Invalid argument to exit   exit 3.14159    exit takes only integer args in the range 0 - 255 (see first footnote)
#128+n  Fatal error signal "n"  kill -9 $PPID of script $? returns 137 (128 + 9)
#255*   Exit status out of range    exit -1 exit takes only integer args in the range 0 - 255
EOF
$(which kill) -l |tr ' ' '\n'| awk '{ printf "S_EX_%s=%s\n", $0, 128+NR; }'

Saya tidak banyak menggunakannya, tetapi yang saya gunakan adalah fungsi shell yang membalikkan kode kesalahan ke format string mereka. Saya sudah menamainya exit2str. Dengan asumsi Anda telah menamai exit.shgenerator di atas exit.sh.sh, kode untuk exit2strdapat dihasilkan dengan ( exit2str.sh.sh):

#!/bin/sh
echo '
exit2str(){
  case "$1" in'
./exit.sh.sh | sed -nEe's|^(S_)?EX_(([^_=]+_?)+)=([0-9]+).*|\4) echo "\1\2";;|p'
echo "
  esac
}"

Saya menggunakan ini di PS1shell interaktif saya sehingga setelah setiap perintah saya jalankan, saya bisa melihat status keluar dan bentuk string-nya (jika memang memiliki bentuk string yang dikenal):

[15:58] pjump@laptop:~ 
(0=OK)$ 
[15:59] pjump@laptop:~ 
(0=OK)$ fdsaf
fdsaf: command not found
[15:59] pjump@laptop:~ 
(127=S_NOENT)$ sleep
sleep: missing operand
Try 'sleep --help' for more information.
[15:59] pjump@laptop:~ 
(1=S_ANY)$ sleep 100
^C
[15:59] pjump@laptop:~ 
(130=S_INT)$ sleep 100
^Z
[1]+  Stopped                 sleep 100
[15:59] pjump@laptop:~ 
(148=S_TSTP)$

Untuk mendapatkan ini, Anda memerlukan insourcable untuk fungsi exit2str:

$ ./exit2str.sh.sh > exit2str.sh #Place this somewhere in your PATH

dan kemudian menggunakannya di Anda ~/.bashrcuntuk menyimpan dan menerjemahkan kode keluar pada setiap command prompt dan menampilkannya prompt Anda ( PS1):

    # ...
    . exit2str.sh
PROMPT_COMMAND='lastStatus=$(st="$?"; echo -n "$st"; str=$(exit2str "$st") && echo "=$str"); # ...'
    PS1="$PS1"'\n($lastStatus)\$'
    # ...                                                                                   

Sangat mudah untuk mengamati bagaimana beberapa program mengikuti konvensi kode keluar dan beberapa tidak, untuk belajar tentang konvensi kode keluar, atau hanya untuk dapat melihat apa yang terjadi lebih mudah. Setelah menggunakannya selama beberapa waktu, saya dapat mengatakan bahwa banyak skrip shell yang berorientasi sistem mengikuti konvensi. EX_USAGEsangat umum, meskipun kode lain, tidak banyak. Saya mencoba mengikuti kebaktian dari waktu ke waktu, meskipun selalu ada $S_EX_ANY(1) untuk orang malas (saya adalah satu).

PSkocik
sumber
Saya bertanya-tanya apakah ada sesuatu seperti pemetaan antara kode errno dan kode keluar untuk digunakan jika kesalahan dilaporkan dengan kode errno menghasilkan keluar kesalahan. Saya mungkin perlu membuat beberapa pemetaan yang masuk akal.
PSkocik
1
Wow! Saya tidak mengharapkan jawaban yang rumit. Saya pasti akan mencobanya sebagai cara yang baik untuk melihat bagaimana berbagai perintah berperilaku. Terima kasih.
Anthony G - keadilan untuk Monica
4

Selama Anda mendokumentasikan kode keluar Anda sehingga Anda mengingatnya satu tahun dari sekarang ketika Anda harus kembali dan mengubah skrip Anda akan baik-baik saja. Gagasan "kode keluar yang dicadangkan" tidak benar-benar berlaku lagi selain untuk mengatakan itu adalah kebiasaan untuk digunakan 0sebagai kode sukses dan apa pun sebagai kode kegagalan.

David King
sumber
4

Referensi terbaik yang bisa saya temukan adalah ini: http://tldp.org/LDP/abs/html/exitcodes.html

Menurut Ini:

1 adalah catchall umum untuk kesalahan, dan saya selalu melihatnya digunakan untuk kesalahan yang ditentukan pengguna.

2 adalah untuk penyalahgunaan shell built in, seperti kesalahan sintaksis

Untuk menjawab pertanyaan Anda secara langsung skrip Anda akan baik-baik saja menggunakan kode kesalahan yang disediakan, itu akan berfungsi seperti yang diharapkan dengan asumsi Anda menangani kesalahan berdasarkan kode kesalahan = 1/2/3.

Namun, mungkin akan membingungkan jika Anda menemukan orang yang tahu dan menggunakan kode kesalahan yang dipesan, yang tampaknya sangat jarang.

Pilihan lain yang tersedia untuk Anda adalah mengulang kesalahan jika ada satu dan kemudian keluar, dengan asumsi skrip Anda mengikuti konvensi Linux "tidak ada berita adalah kabar baik" dan tidak ada gema tentang kesuksesan.

if [ $? -ne 0 ];then
    echo "Error type"
    exit 1
fi
Centimane
sumber
2

Berdasarkan jawaban yang saya terima (sulit untuk memilih salah satu dari yang lain), tidak berbahaya untuk menunjukkan jenis kesalahan tertentu dengan menggunakan kode keluar yang juga digunakan Bash. Bash (atau shell Unix lainnya) tidak akan melakukan sesuatu yang istimewa (seperti menjalankan penangan pengecualian) jika skrip pengguna keluar dengan salah satu kode kesalahan ini.

Tampaknya penulis Advanced Bash-Scripting Guide setuju dengan upaya BSD untuk membakukan kode keluar ( sysexits.h) dan hanya merekomendasikan bahwa ketika pengguna menulis skrip shell, mereka tidak menentukan kode keluar yang bertentangan dengan kode keluar yang sudah ditentukan sebelumnya. digunakan, yaitu, mereka membatasi kode keluar khusus mereka ke 50 kode status yang tersedia dalam kisaran 64-113.

Saya menghargai ide (dan alasannya) tetapi saya lebih suka jika penulis lebih eksplisit bahwa tidak berbahaya untuk mengabaikan saran - selain dari kasus di mana konsumen skrip sedang memeriksa kesalahan seperti contoh yang dikutip dari 127 ( command not found).

Spesifikasi POSIX yang relevan

Saya meneliti apa yang dikatakan POSIX tentang kode keluar dan spesifikasi POSIX tampaknya sesuai dengan penulis Advanced Bash-Scripting Guide. Saya telah mengutip spesifikasi POSIX yang relevan (penekanan saya):

Status Keluar untuk Perintah

Setiap perintah memiliki status keluar yang dapat mempengaruhi perilaku perintah shell lainnya. Status keluar dari perintah yang bukan utilitas didokumentasikan dalam bagian ini. Status keluar dari utilitas standar didokumentasikan di bagian masing-masing.

Jika perintah tidak ditemukan, status keluar harus 127. Jika nama perintah ditemukan, tetapi itu bukan utilitas yang dapat dieksekusi, status keluar harus 126. Aplikasi yang memanggil utilitas tanpa menggunakan shell harus menggunakan nilai status keluar ini. melaporkan kesalahan serupa.

Jika suatu perintah gagal selama ekspansi kata atau redirection, status keluarnya harus lebih besar dari nol.

Secara internal, untuk keperluan memutuskan apakah suatu perintah keluar dengan status keluar bukan nol, shell harus mengenali seluruh nilai status yang diambil untuk perintah tersebut dengan ekivalen fungsi tunggu () makro WEXITSTATUS (sebagaimana didefinisikan dalam volume Antarmuka Sistem dari POSIX.1-2008). Saat melaporkan status keluar dengan parameter khusus '?', Shell harus melaporkan delapan bit penuh status keluar yang tersedia. Status keluar dari perintah yang diakhiri karena menerima sinyal harus dilaporkan lebih besar dari 128.

The exitutilitas

Sebagaimana dijelaskan di bagian lain, nilai status keluar tertentu telah dicadangkan untuk penggunaan khusus dan harus digunakan oleh aplikasi hanya untuk tujuan tersebut:

  • 126 - File yang akan dieksekusi ditemukan, tetapi itu bukan utilitas yang dapat dieksekusi.
  • 127 - Utilitas yang akan dieksekusi tidak ditemukan.
  • >128 - Perintah terputus oleh sinyal.

Informasi lebih lanjut

Untuk apa nilainya, saya dapat memverifikasi semua kecuali satu dari daftar Kode Keluar Dengan Makna Khusus . Tabel kode keluar ini bermanfaat karena memberikan rincian lebih lanjut - dan contoh cara membuat kode kesalahan yang didokumentasikan dalam referensi Bash .

Mencoba menghasilkan status keluar dari 128

Menggunakan Bash versi 3.2.25 dan 4.2.46, saya mencoba untuk melemparkan 128 Invalid argument to exitkesalahan tetapi setiap kali saya menerima 255 (Keluar status di luar jangkauan). Misalnya, jika exit 3.14159dijalankan sebagai bagian dari skrip shell atau dalam shell anak interaktif, shell keluar dengan kode 255:

$ exit 3.14159
exit
bash: exit: 3.14159: numeric argument required

Untuk lebih menyenangkan, saya juga mencoba menjalankan program C sederhana tetapi dalam kasus ini, tampaknya exit(3)fungsinya hanya mengkonversi float ke int (3 dalam hal ini) sebelum keluar:

#include <stdlib.h>
main()
{
    exit(3.14159);
}
Anthony G - keadilan untuk Monica
sumber