Ini didokumentasikan (untuk POSIX) di Bagian 2.9.1 Perintah Sederhana
dari Spesifikasi Basis Grup Terbuka. Ada dinding teks di sana; Saya mengarahkan perhatian Anda ke paragraf terakhir:
Jika ada nama perintah, eksekusi harus dilanjutkan seperti yang dijelaskan dalam Pencarian dan Eksekusi Perintah . Jika tidak ada nama perintah, tetapi perintah berisi substitusi perintah, perintah harus melengkapi dengan status keluar dari substitusi perintah terakhir yang dilakukan. Jika tidak, perintah harus lengkap dengan status keluar nol.
Jadi, misalnya,
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
Ini juga cara bash bekerja. Tetapi lihat juga bagian “tidak begitu sederhana” di bagian akhir.
PHK , dalam pertanyaannya Penugasan seperti perintah dengan status keluar kecuali ketika ada substitusi perintah? , menyarankan
... tampaknya seolah-olah suatu tugas itu sendiri dihitung sebagai perintah ... dengan nilai keluar nol, tetapi yang berlaku sebelum sisi kanan tugas (misalnya, panggilan substitusi perintah ...)
Itu bukan cara yang mengerikan untuk melihatnya. Sebuah skema mentah untuk menentukan status kembalinya perintah sederhana (satu tidak mengandung ;
, &
, |
, &&
atau ||
) adalah:
- Pindai garis dari kiri ke kanan hingga Anda mencapai akhir atau kata perintah (biasanya nama program).
- Jika Anda melihat tugas variabel, status pengembalian untuk baris mungkin saja 0.
- Jika Anda melihat substitusi perintah - yaitu,
$(…)
- ambil status keluar dari perintah itu.
- Jika Anda mencapai perintah yang sebenarnya (bukan di substitusi perintah), ambil status keluar dari perintah itu.
Status pengembalian untuk baris adalah nomor terakhir yang Anda temui.
Pergantian perintah sebagai argumen untuk perintah, misalnya, foo $(bar)
tidak masuk hitungan; Anda mendapatkan status keluar dari foo
. Mengutip notasi phk , perilaku di sini adalah
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Tapi ini sedikit penyederhanaan. Status pengembalian keseluruhan dari
A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
adalah status keluar dari . The tugas yang terjadi setelah tugas tidak menetapkan status keluar keseluruhan untuk 0.
cmd4
E=
D=
icarus , dalam jawabannya untuk pertanyaan phk , memunculkan poin penting: variabel dapat diatur sebagai hanya dibaca. Paragraf ketiga hingga terakhir dalam Bagian 2.9.1 dari standar POSIX mengatakan,
Jika salah satu dari penugasan variabel mencoba untuk memberikan nilai ke variabel yang atribut readonly diatur dalam lingkungan shell saat ini (terlepas dari apakah penugasan dibuat di lingkungan itu), kesalahan penugasan variabel akan terjadi. Lihat Konsekuensi Kesalahan Shell untuk konsekuensi dari kesalahan ini.
jadi jika Anda mengatakannya
readonly A
C=Garfield A=Felix T=Tigger
status kembali adalah 1. Tidak peduli jika string Garfield
, Felix
dan / atau Tigger
diganti dengan perintah substitusi (s) - tetapi lihat catatan di bawah.
Bagian 2.8.1 Konsekuensi Kesalahan Shell memiliki banyak teks, dan sebuah tabel, dan diakhiri dengan
Dalam semua kasus yang ditunjukkan pada tabel di mana shell interaktif diperlukan untuk tidak keluar, shell tidak akan melakukan pemrosesan lebih lanjut dari perintah di mana kesalahan terjadi.
Beberapa detail masuk akal; beberapa tidak:
- The
A=
tugas kadang-kadang dibatalkan baris perintah, sebagai kalimat terakhir tampaknya menentukan. Dalam contoh di atas, C
diatur ke Garfield
, tetapi T
tidak diatur (dan, tentu saja, tidak juga A
).
- Demikian pula,
mengeksekusi
tetapi tidak . Tapi, dalam versi saya bash (termasuk 4.1.x dan 4.3.X), itu tidak mengeksekusi . (Kebetulan, ini lebih lanjut mengimplikasikan interpretasi phk bahwa nilai keluar dari penugasan berlaku sebelum sisi kanan penugasan.)
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Tapi ini kejutan:
Dalam versi bash saya,
baca saja A
C = sesuatu A = sesuatu T = sesuatu cmd 0
tidak mengeksekusi. Khususnya,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
mengeksekusi
dan , tetapi tidak . (Perhatikan bahwa ini adalah kebalikan dari perilakunya ketika tidak ada perintah.) Dan itu menetapkan (dan juga ) di lingkungan . Saya bertanya-tanya apakah ini bug di bash.
cmd1
cmd3
cmd2
T
C
cmd0
Tidak sesederhana itu:
Paragraf pertama dari jawaban ini mengacu pada "perintah sederhana".
Spesifikasi mengatakan,
"Perintah sederhana" adalah urutan penugasan dan pengalihan variabel opsional, dalam urutan apa pun, secara opsional diikuti oleh kata-kata dan pengalihan, diakhiri oleh operator kontrol.
Ini adalah pernyataan seperti yang ada di blok contoh pertama saya:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
tiga yang pertama termasuk tugas variabel, dan tiga yang terakhir termasuk penggantian perintah.
Tetapi beberapa tugas variabel tidak begitu sederhana.
bash (1) mengatakan,
Pernyataan penugasan juga dapat muncul sebagai argumen untuk alias
, declare
, typeset
, export
, readonly
, dan local
builtin perintah ( deklarasi perintah).
Sebab export
, spesifikasi POSIX mengatakan,
EXIT STATUS
0Semua operan nama berhasil diekspor.
> 0Setidaknya satu nama tidak dapat diekspor, atau -p
opsi ditentukan dan terjadi kesalahan.
Dan POSIX tidak mendukung local
, tetapi bash (1) mengatakan,
Ini adalah kesalahan untuk digunakan local
saat tidak dalam suatu fungsi. Status pengembalian adalah 0 kecuali local
digunakan di luar fungsi, nama yang tidak valid diberikan, atau nama adalah variabel readonly.
Membaca yang tersirat, kita bisa melihat bahwa perintah deklarasi suka
export FOO=$(bar)
dan
local FOO=$(bar)
lebih seperti
foo $(bar)
sejauh mereka mengabaikan status keluar dari bar
dan memberikan status keluar berdasarkan perintah utama ( export
, local
, atau foo
). Jadi kita punya keanehan seperti
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
yang bisa kita tunjukkan dengan
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
dan
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
Untungnya ShellCheck menangkap kesalahan dan memunculkan SC2155 , yang menyarankan itu
export foo="$(mycmd)"
harus diubah menjadi
foo=$(mycmd)
export foo
dan
local foo="$(mycmd)"
harus diubah menjadi
local foo
foo=$(mycmd)
local
hubungan ini? Misalnyalocal foo=$(bar)
?