Ghi chú
Tôi đang đưa ra một câu trả lời tập trung vào Bash vì bash
thẻ.
Câu trả lời ngắn
Miễn là bạn chỉ xử lý các biến được đặt tên trong Bash, hàm này sẽ luôn cho bạn biết nếu biến đã được đặt, ngay cả khi đó là một mảng trống.
variable-is-set() {
declare -p "$1" &>/dev/null
}
Tại sao điều này hoạt động
Trong Bash (ít nhất là từ 3.0), nếu var
là biến được khai báo / set, sau đó declare -p var
xuất ra một declare
lệnh sẽ đặt biến var
thành bất kỳ loại và giá trị hiện tại nào và trả về mã trạng thái 0
(thành công). Nếu var
không được khai báo, sau đó declare -p var
xuất ra một thông báo lỗi stderr
và trả về mã trạng thái 1
. Sử dụng &>/dev/null
, chuyển hướng cả thông thường stdout
và stderr
đầu ra /dev/null
, không bao giờ được nhìn thấy và không thay đổi mã trạng thái. Do đó, hàm chỉ trả về mã trạng thái.
Tại sao các phương thức khác (đôi khi) thất bại trong Bash
[ -n "$var" ]
: Điều này chỉ kiểm tra nếu ${var[0]}
không trống. (Trong Bash, $var
giống như ${var[0]}
.)
[ -n "${var+x}" ]
: Điều này chỉ kiểm tra nếu ${var[0]}
được đặt.
[ "${#var[@]}" != 0 ]
: Điều này chỉ kiểm tra nếu ít nhất một chỉ mục $var
được đặt.
Khi phương thức này thất bại ở Bash
Đây chỉ hoạt động cho các biến được đặt tên (bao gồm $_
), không chắc chắn biến đặc biệt ( $!
, $@
, $#
, $$
, $*
, $?
, $-
, $0
, $1
, $2
, ..., và bất kỳ tôi có thể đã quên). Vì không có cái nào trong số này là mảng, kiểu POSIX [ -n "${var+x}" ]
hoạt động cho tất cả các biến đặc biệt này. Nhưng hãy cẩn thận khi gói nó trong một hàm vì nhiều biến đặc biệt thay đổi giá trị / tồn tại khi các hàm được gọi.
Shell tương thích
Nếu tập lệnh của bạn có mảng và bạn đang cố gắng làm cho nó tương thích với càng nhiều shell càng tốt, thì hãy cân nhắc sử dụng typeset -p
thay vì declare -p
. Tôi đã đọc rằng ksh chỉ hỗ trợ trước đây, nhưng không thể kiểm tra điều này. Tôi biết rằng Bash 3.0+ và Zsh 5.5.1 đều hỗ trợ cả hai typeset -p
và declare -p
, chỉ khác nhau ở chỗ cái này là cái thay thế cho cái kia. Nhưng tôi đã không kiểm tra sự khác biệt ngoài hai từ khóa đó và tôi đã không kiểm tra các trình bao khác.
Nếu bạn cần tập lệnh của mình tương thích với POSIX sh, thì bạn không thể sử dụng mảng. Không có mảng, [ -n "{$var+x}" ]
công trình.
Mã so sánh cho các phương thức khác nhau trong Bash
Hàm này bỏ đặt biến var
, eval
là mã đã qua, chạy thử nghiệm để xác định xem var
có được đặt bởi eval
mã d hay không, và cuối cùng hiển thị mã trạng thái kết quả cho các thử nghiệm khác nhau.
Tôi bỏ qua test -v var
, [ -v var ]
và [[ -v var ]]
vì họ mang lại kết quả giống với tiêu chuẩn POSIX [ -n "${var+x}" ]
, trong khi đòi hỏi Bash 4.2 trở lên. Tôi cũng đang bỏ quatypeset -p
vì nó giống như declare -p
trong các shell mà tôi đã thử nghiệm (Bash 3.0 đến 5.0 và 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
}
Mã trường hợp thử nghiệm
Lưu ý rằng kết quả kiểm tra có thể bất ngờ do Bash coi các chỉ số mảng không phải là số "0" nếu biến không được khai báo là mảng kết hợp. Ngoài ra, mảng kết hợp chỉ có giá trị trong 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
Đầu ra thử nghiệm
Các thuật nhớ kiểm tra trong tương ứng với tiêu đề hàng đến [ -n "$var" ]
, [ -n "${var+x}" ]
, [ "${#var[@]}" != 0 ]
, và declare -p var
, tương ứng.
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
Tóm lược
declare -p var &>/dev/null
là (100%?) đáng tin cậy để kiểm tra các biến có tên trong Bash kể từ ít nhất 3.0.
[ -n "${var+x}" ]
là đáng tin cậy trong các tình huống tuân thủ POSIX, nhưng không thể xử lý các mảng.
- Các thử nghiệm khác tồn tại để kiểm tra nếu một biến là không trống và để kiểm tra các biến được khai báo trong các shell khác. Nhưng các thử nghiệm này phù hợp với cả các kịch bản Bash và POSIX.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.