Làm thế nào để kiểm tra nếu một biến được định nghĩa hoàn toàn trong Bash trước phiên bản 4.2 với tùy chọn shell nounset?


16

Đối với các phiên bản Bash trước "GNU bash, Phiên bản 4.2" có bất kỳ lựa chọn thay thế tương đương nào cho -vtùy chọn của testlệnh không? Ví dụ:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo

-vkhông phải là một lựa chọn test, nhưng một toán tử cho các biểu thức điều kiện.
Tim

@Tim Đó là ba điều, bên cạnh việc là mã thông báo, chuỗi và một phần của dòng: An optionto a command test -v, an operatorto a conditional expressionvà a unary test primaryfor [ ]. Đừng trộn ngôn ngữ tiếng Anh với định nghĩa shell.

Câu trả lời:


36

Di động đến tất cả các vỏ POSIX:

if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi

Làm điều đó ${foobar:+1}nếu bạn muốn đối xử foobartheo cùng một cách cho dù nó trống hay không được xác định. Bạn cũng có thể sử dụng ${foobar-}để có được một chuỗi trống khi foobarkhông xác định và giá trị foobarkhác (hoặc đặt bất kỳ giá trị mặc định nào khác sau -).

Trong ksh, nếu foobarđược khai báo nhưng không được xác định, như trong typeset -a foobar, sau đó ${foobar+1}mở rộng thành chuỗi rỗng.

Zsh không có các biến được khai báo nhưng không được đặt: typeset -a foobartạo một mảng trống.

Trong bash, mảng hoạt động theo một cách khác và đáng ngạc nhiên. ${a+1}chỉ mở rộng thành 1nếu alà một mảng không trống, vd

typeset -a a; echo ${a+1}    # prints nothing
e=(); echo ${e+1}            # prints nothing!
f=(''); echo ${f+1}          # prints 1

Nguyên tắc tương tự áp dụng cho các mảng kết hợp: các biến mảng được xử lý như được định nghĩa nếu chúng có một bộ chỉ mục không trống.

Một cách khác để kiểm tra cụ thể về bash xem một biến thuộc bất kỳ loại nào đã được xác định hay chưa là kiểm tra xem nó có được liệt kê trong đó không . Điều này báo cáo các mảng trống như được định nghĩa, không giống như , nhưng báo cáo các biến được khai báo nhưng không được gán ( ) là không xác định.${!PREFIX*}${foobar+1}unset foobar; typeset -a foobar

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is defined";;
  *) echo "foobar is not defined";;
esac

Điều này tương đương với việc kiểm tra giá trị trả về của typeset -p foobarhoặcdeclare -p foobar , ngoại trừ typeset -p foobarthất bại đối với các biến được khai báo nhưng không được gán.

Trong bash, như trong ksh, set -o nounset; typeset -a foobar; echo $foobargây ra lỗi trong nỗ lực mở rộng biến không xác định foobar. Không giống như trong ksh, set -o nounset; foobar=(); echo $foobar(hoặc echo "${foobar[@]}") cũng gây ra lỗi.

Lưu ý rằng trong tất cả các tình huống được mô tả ở đây, ${foobar+1}mở rộng sang chuỗi trống khi và chỉ khi $foobarsẽ gây ra lỗi bên dưới set -o nounset.


1
Còn mảng thì sao? Trong phiên bản Bash "GNU bash, phiên bản 4.1.10 (4) -release (i686-pc-cygwin)" echo "${foobar:+1}"không in 1nếu declare -a foobarđược phát hành trước đó và do đó foobarlà một mảng được lập chỉ mục. declare -p foobarbáo cáo chính xác declare -a foobar='()'. Có "${foobar:+1}"chỉ làm việc cho các biến không mảng?
Tim Friske

@TimFriske ${foobar+1}(không có :, tôi đã đảo ngược hai ví dụ trong câu trả lời ban đầu của tôi) là chính xác cho các mảng trong bash nếu định nghĩa của bạn về Định nghĩa xác định là một cách $foobarhiệu quả set -o nounset. Nếu định nghĩa của bạn là khác nhau, bash là một chút lạ. Xem câu trả lời cập nhật của tôi.
Gilles 'SO- ngừng trở nên xấu xa'

1
Liên quan đến chủ đề "Trong bash, mảng hoạt động theo một cách khác và đáng ngạc nhiên." hành vi có thể được giải thích từ các trang bash (1), phần "Mảng". Nó tuyên bố rằng "Tham chiếu một biến mảng không có chỉ mục con tương đương với tham chiếu mảng có chỉ số là 0.". Do đó, nếu cả 0chỉ mục và khóa không được xác định là đúng a=(), thì ${a+1}trả về chính xác không có gì.
Tim Friske

1
@TimFriske Tôi biết rằng việc thực hiện bash phù hợp với tài liệu của nó. Nhưng đối xử với một mảng trống như một biến không xác định là thiết kế thực sự kỳ lạ.
Gilles 'SO- ngừng trở nên xấu xa'

Tôi thích kết quả từ: Làm thế nào để kiểm tra nếu một biến được đặt trong Bash? -> Đúng cách ... Tôi tạo ra một hợp âm với cách tôi làm việc này nên hoạt động. Dám tôi nói, Windows có một definednhà điều hành. Test có thể làm điều đó; không thể khó được ( ừm ....)
sẽ vào

5

Để tóm tắt câu trả lời của Gilles, tôi đã tạo ra các quy tắc sau:

  1. Sử dụng [[ -v foobar ]]cho các biến trong phiên bản Bash> = 4.2.
  2. Sử dụng declare -p foobar &>/dev/nullcho các biến mảng trong phiên bản Bash <4.2.
  3. Sử dụng (( ${foo[0]+1} ))hoặc (( ${bar[foo]+1} ))cho các chỉ số của mảng được lập chỉ mục ( -a) và keyed ( -A) ( declare). Tùy chọn 1 và 2 không hoạt động ở đây.

3

Tôi sử dụng cùng một kỹ thuật cho tất cả các biến trong bash và nó hoạt động, ví dụ:

[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

đầu ra:

foobar is unset

trong khi

foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

đầu ra:

foobar is set

Phải xóa [@] nếu mảng có nhiều hơn một giá trị.
Stein Inge Morisbak

1
Điều này hoạt động rất tốt, miễn là bạn muốn kiểm tra xem nó có giá trị hay không, liệu nó có được xác định hay không. Tức là, foobar=""sau đó sẽ báo cáo rằng foobar is unset. Không chờ đợi, tôi lấy lại. Thực sự chỉ kiểm tra xem phần tử đầu tiên có trống hay không, vì vậy nó dường như chỉ là một ý tưởng tốt nếu bạn biết biến KHÔNG phải là một mảng và bạn chỉ quan tâm đến sự trống rỗng, không xác định.
Ron Burk

chỉ hoạt động nếu tập lệnh của bạn đang chạy với các biến không xác định được cho phép (không có tập -u)
Florian Heigl
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.