Catatan
Saya memberikan jawaban yang sangat terfokus pada Bash karena bash
tag tersebut.
Jawaban singkat
Selama Anda hanya berurusan dengan variabel bernama di Bash, fungsi ini harus selalu memberi tahu Anda jika variabel telah ditetapkan, bahkan jika itu adalah array kosong.
variable-is-set() {
declare -p "$1" &>/dev/null
}
Kenapa ini bekerja?
Dalam Bash (setidaknya sejauh 3.0), jika var
merupakan variabel yang dideklarasikan / ditetapkan, maka declare -p var
output sebuah declare
perintah yang akan mengatur variabel var
untuk apa pun jenis dan nilainya saat ini, dan mengembalikan kode status 0
(sukses). Jika var
tidak dideklarasikan, kemudian declare -p var
menampilkan pesan kesalahan ke stderr
dan mengembalikan kode status 1
. Menggunakan &>/dev/null
, mengalihkan baik reguler stdout
dan stderr
output ke /dev/null
, tidak pernah terlihat, dan tanpa mengubah kode status. Dengan demikian fungsi hanya mengembalikan kode status.
Mengapa metode lain (kadang-kadang) gagal di Bash
[ -n "$var" ]
: Ini hanya memeriksa jika ${var[0]}
tidak kosong. (Dalam Bash, $var
sama dengan ${var[0]}
.)
[ -n "${var+x}" ]
: Ini hanya memeriksa jika ${var[0]}
diatur.
[ "${#var[@]}" != 0 ]
: Ini hanya memeriksa jika setidaknya satu indeks $var
diatur.
Ketika metode ini gagal di Bash
Ini hanya bekerja untuk variabel bernama (termasuk $_
), tidak variabel khusus tertentu ( $!
, $@
, $#
, $$
, $*
, $?
, $-
, $0
, $1
, $2
, ..., dan setiap saya mungkin sudah lupa). Karena tidak satupun dari ini adalah array, gaya POSIX [ -n "${var+x}" ]
berfungsi untuk semua variabel khusus ini. Tetapi berhati-hatilah untuk membungkusnya dalam suatu fungsi karena banyak variabel khusus mengubah nilai / keberadaan ketika fungsi dipanggil.
Catatan kompatibilitas shell
Jika naskah Anda memiliki array dan Anda mencoba untuk membuatnya kompatibel dengan banyak kerang mungkin, maka pertimbangkan untuk menggunakan typeset -p
bukan declare -p
. Saya pernah membaca bahwa ksh hanya mendukung yang pertama, tetapi belum dapat menguji ini. Saya tahu bahwa Bash 3.0+ dan Zsh 5.5.1 masing-masing mendukung keduanya , typeset -p
dan declare -p
hanya berbeda satu yang merupakan alternatif untuk yang lain. Tapi saya belum menguji perbedaan di luar dua kata kunci tersebut, dan saya belum menguji shell lain.
Jika Anda membutuhkan skrip agar POSIX sh kompatibel, maka Anda tidak dapat menggunakan array. Tanpa array, [ -n "{$var+x}" ]
berfungsi.
Kode perbandingan untuk berbagai metode di Bash
Fungsi ini menghapus variabel var
, eval
sebagai kode yang diteruskan, menjalankan tes untuk menentukan apakah var
diatur oleh eval
kode d, dan akhirnya menunjukkan kode status yang dihasilkan untuk pengujian yang berbeda.
Saya melewatkan test -v var
,, [ -v var ]
dan [[ -v var ]]
karena mereka menghasilkan hasil yang identik dengan standar POSIX [ -n "${var+x}" ]
, sementara membutuhkan Bash 4.2+. Saya juga melewatkan typeset -p
karena sama seperti declare -p
di shell saya sudah diuji (Bash 3.0 hingga 5.0, dan Zsh 5.5.1).
is-var-set-after() {
# Set var by passed expression.
unset var
eval "$1"
# Run the tests, in increasing order of accuracy.
[ -n "$var" ] # (index 0 of) var is nonempty
nonempty=$?
[ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
plus=$?
[ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
count=$?
declare -p var &>/dev/null # var has been declared (any type)
declared=$?
# Show test results.
printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}
Kode uji kasus
Perhatikan bahwa hasil tes mungkin tidak terduga karena Bash memperlakukan indeks array non-numerik sebagai "0" jika variabel belum dinyatakan sebagai array asosiatif. Juga, array asosiatif hanya berlaku di Bash 4.0+.
# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo" # 0 0 0 0
is-var-set-after "var=(foo)" # 0 0 0 0
is-var-set-after "var=([0]=foo)" # 0 0 0 0
is-var-set-after "var=([x]=foo)" # 0 0 0 0
is-var-set-after "var=([y]=bar [x]=foo)" # 0 0 0 0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''" # 1 0 0 0
is-var-set-after "var=([0]='')" # 1 0 0 0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')" # 1 1 0 0
is-var-set-after "var=([1]=foo)" # 1 1 0 0
is-var-set-after "declare -A var; var=([x]=foo)" # 1 1 0 0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()" # 1 1 1 0
is-var-set-after "declare -a var" # 1 1 1 0
is-var-set-after "declare -A var" # 1 1 1 0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var" # 1 1 1 1
Uji keluaran
Tes mnemonik dalam berkorespondensi baris header untuk [ -n "$var" ]
, [ -n "${var+x}" ]
, [ "${#var[@]}" != 0 ]
, dan declare -p var
masing-masing.
test: -n +x #@ -p
var=foo: 0 0 0 0
var=(foo): 0 0 0 0
var=([0]=foo): 0 0 0 0
var=([x]=foo): 0 0 0 0
var=([y]=bar [x]=foo): 0 0 0 0
var='': 1 0 0 0
var=([0]=''): 1 0 0 0
var=([1]=''): 1 1 0 0
var=([1]=foo): 1 1 0 0
declare -A var; var=([x]=foo): 1 1 0 0
var=(): 1 1 1 0
declare -a var: 1 1 1 0
declare -A var: 1 1 1 0
unset var: 1 1 1 1
Ringkasan
declare -p var &>/dev/null
apakah (100%?) dapat diandalkan untuk menguji variabel bernama di Bash sejak setidaknya 3,0.
[ -n "${var+x}" ]
dapat diandalkan dalam situasi yang sesuai dengan POSIX, tetapi tidak dapat menangani array.
- Tes lain ada untuk memeriksa apakah suatu variabel adalah kosong, dan untuk memeriksa variabel yang dideklarasikan dalam shell lain. Tetapi tes ini tidak cocok untuk skrip Bash atau POSIX.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.