Saya memiliki skrip, yang tidak keluar ketika saya menginginkannya.
Contoh skrip dengan kesalahan yang sama adalah:
#!/bin/bash
function bla() {
return 1
}
bla || ( echo '1' ; exit 1 )
echo '2'
Saya akan berasumsi untuk melihat output:
:~$ ./test.sh
1
:~$
Tapi saya benar-benar melihat:
:~$ ./test.sh
1
2
:~$
Apakah ()
perintah chaining entah bagaimana menciptakan ruang lingkup? Apa yang exit
keluar dari, jika bukan skrip?
shell-script
exit
subshell
Minix
sumber
sumber
Jawaban:
()
menjalankan perintah dalam subkulit, sehinggaexit
Anda keluar dari subkulit dan kembali ke shell induk. Gunakan kawat gigi{}
jika Anda ingin menjalankan perintah di shell saat ini.Dari manual bash:
Patut disebutkan bahwa sintaks shell cukup konsisten dan subkulit berpartisipasi juga dalam
()
konstruksi lain seperti substitusi perintah (juga dengan`..`
sintaks gaya lama ) atau proses substitusi, sehingga yang berikut tidak akan keluar dari shell saat ini:Meskipun mungkin jelas bahwa subkulit terlibat ketika perintah ditempatkan secara eksplisit di dalam
()
, fakta yang kurang terlihat adalah bahwa mereka juga muncul dalam struktur lain ini:perintah dimulai di latar belakang
tidak keluar dari shell saat ini karena (setelah
man bash
)pipa
masih keluar hanya dari subkulit.
Namun berbagai kerang berperilaku berbeda dalam hal ini. Misalnya
bash
menempatkan semua komponen pipa ke subkulit yang terpisah (kecuali jika Anda menggunakanlastpipe
opsi dalam doa di mana kontrol pekerjaan tidak diaktifkan), tetapi AT&Tksh
danzsh
jalankan bagian terakhir di dalam shell saat ini (kedua perilaku diizinkan oleh POSIX). Jadipada dasarnya tidak melakukan apa pun di bash, tetapi keluar dari zsh karena yang terakhir
exit
.coproc exit
juga berjalanexit
dalam subkulit.sumber
{
dan}
bukan sintaks, mereka adalah kata-kata yang dicadangkan dan harus dikelilingi oleh spasi, dan daftar harus diakhiri dengan terminator perintah (titik koma, baris baru, ampersand)(echo $$)
mencetak shell id induk karena$$
diperluas bahkan sebelum subkulit dibuat. Faktanya mencetak id proses subkulit bisa jadi rumit, lihat stackoverflow.com/questions/9119885/…$$
diperluas sebelum subkulit dibuat, dan belum$BASHPID
menunjukkan nilai yang benar untuk subkulit?Menjalankan
exit
subkulit adalah satu perangkap:Script mencetak 42, keluar dari subkulit dengan kode kembali
1
, dan melanjutkan dengan skrip. Bahkan mengganti panggilan denganecho $(CALC) || exit 1
tidak membantu karena kode balikecho
adalah 0 terlepas dari kode balikcalc
. Dancalc
dieksekusi sebelumecho
.Yang lebih membingungkan adalah menggagalkan efek
exit
dengan membungkusnya menjadilocal
builtin seperti pada script berikut. Saya tersandung masalah ketika saya menulis fungsi untuk memverifikasi nilai input. Contoh:Saya ingin membuat file bernama "year month day.log", yaitu
20141211.log
untuk hari ini. Tanggal tersebut diinput oleh pengguna yang mungkin gagal memberikan nilai wajar. Oleh karena itu, dalam fungsi saya,fname
saya memeriksa nilai balikdate
untuk memverifikasi validitas input pengguna:Kelihatan bagus. Biarkan skrip diberi nama
s.sh
. Jika pengguna memanggil skrip./s.sh "Thu Dec 11 20:45:49 CET 2014"
, file20141211.log
dibuat. Namun, jika tipe pengguna./s.sh "Thu hec 11 20:45:49 CET 2014"
, maka skrip menghasilkan:Baris
fname…
mengatakan bahwa data input buruk telah terdeteksi di subkulit. Tetapiexit 1
pada akhirlocal …
baris tidak pernah dipicu karenalocal
arahan selalu kembali0
. Ini karenalocal
dieksekusi setelah$(fname)
dan dengan demikian menimpa kode kembali. Dan karena itu, skrip berlanjut dan memanggiltouch
dengan parameter kosong. Contoh ini sederhana tetapi perilaku bash bisa sangat membingungkan dalam aplikasi nyata. Saya tahu, programmer sebenarnya tidak menggunakan penduduk setempatUntuk memperjelas: Tanpa
local
, skrip dibatalkan seperti yang diharapkan ketika tanggal yang tidak valid dimasukkan.Cara mengatasinya adalah dengan membagi garis seperti
Perilaku aneh sesuai dengan dokumentasi
local
di dalam halaman manual bash: "Status pengembalian adalah 0 kecuali lokal digunakan di luar fungsi, nama tidak valid diberikan, atau nama adalah variabel readonly."Meskipun bukan bug, saya merasa bahwa perilaku bash adalah berlawanan dengan intuisi. Saya menyadari urutan eksekusi,
local
tidak harus menutupi tugas yang rusak, namun.Jawaban awal saya berisi beberapa ketidaktepatan. Setelah diskusi yang mendalam dan mendalam dengan mikeserv (terima kasih untuk itu) saya pergi untuk memperbaikinya.
sumber
doit()
.Solusi aktual:
Pengelompokan kesalahan hanya akan dijalankan jika
bla
mengembalikan status kesalahan, danexit
tidak dalam subkulit sehingga seluruh skrip berhenti.sumber
Tanda kurung memulai subkulit dan keluar hanya keluar dari subkulit itu.
Anda dapat membaca kode akses dengan
$?
dan menambahkan ini dalam skrip Anda untuk keluar dari skrip jika subshell keluar:sumber