Làm cách nào để kiểm tra xem một biến có tồn tại trong câu lệnh 'if' không?


69

Tôi cần kiểm tra sự tồn tại của một biến trong một iftuyên bố. Một cái gì đó có tác dụng của:

if [ -v $somevar ]
then
    echo "Variable somevar exists!"
else
    echo "Variable somevar does not exist!"

Và câu hỏi gần nhất là đây , câu hỏi không thực sự trả lời câu hỏi của tôi.


Nếu bạn muốn đặt thành $somevargiá trị / chuỗi nếu biến không tồn tại : ${somevar:=42}.
Cyrus

Cá nhân, tôi có xu hướng chỉ kiểm tra sự trống rỗng ( [ -n "$var" ]hoặc [ ! -z "$var" ]). Tôi nghĩ rằng kiểm tra sự tồn tại / không tồn tại là quá tinh tế, và tôi thích mã của tôi thô và đơn giản.
PSkocik

Tại sao bạn cần điều đó vs [ -n "$var" ]? Liên quan: stackoverflow.com/questions/3601515/...
Ciro Santilli新疆改造中心法轮功六四事件

Câu trả lời:


97

Trong bash hiện đại (phiên bản 4.2 trở lên):

[[ -v name_of_var ]]

Từ help test:

-v VAR, Đúng nếu VAR biến shell được đặt


3
Cũng hoạt động với dấu ngoặc đơn : [ -v name_of_var ].
meuh

7
hãy cẩn thận với băm và mảng, nó trả về false trừ khi biến có phần tử là khóa / chỉ số "0". Đối với namerefs, nó kiểm tra xem mục tiêu được xác định. Nó không làm việc cho các thông số đặc biệt như $1, $-, $#...
Stéphane Chazelas

4
Tính năng này chỉ có trong bash dựng sẵn testhoặc [; Nó không có sẵn trong /usr/bin/test. So sánh man testvới help test.
Mark Lakata

@MarkLakata Phải, vì các lệnh bên ngoài không thể biết trạng thái bên trong của shell.
Chris Xuống

umm không nên luôn luôn là [[-v "$ name_of_var"]]?
Alexander Mills

24

Phụ thuộc vào những gì bạn có nghĩa là tồn tại .

Có một biến đã được khai báo nhưng không được chỉ định tồn tại ?

Có một biến mảng (hoặc băm) đã được chỉ định một danh sách trống tồn tại không?

Có một biến nameref trỏ đến một biến hiện không được chỉ định tồn tại ?

Bạn có xem xét $-, $#, $1biến? (POSIX không).

Trong các vỏ giống như Bourne, cách chính tắc là:

if [ -n "${var+set}" ]; then
  echo '$var was set'
fi

Điều đó làm việc cho các biến vô hướng và các thông số khác cho biết nếu một biến đã được gán một giá trị (trống hay không, tự động, từ môi trường, assigments, read, forhoặc khác).

Đối với các shell có a typesethoặc declarelệnh, điều đó sẽ không báo cáo như đặt các biến đã được khai báo nhưng không được chỉ định ngoại trừ trong zsh.

Đối với các shell hỗ trợ các mảng, ngoại trừ yashzshsẽ không báo cáo là các biến mảng được đặt trừ khi phần tử của chỉ số 0 đã được đặt.

Đối với bash(nhưng không phải ksh93cũng không zsh), đối với các biến của mảng kết hợp kiểu , điều đó sẽ không báo cáo chúng là tập hợp trừ khi phần tử khóa "0" của chúng đã được đặt.

Đối với ksh93bash, đối với các biến có kiểu nameref , điều đó chỉ trả về true nếu biến được tham chiếu bởi nameref được coi là tập hợp .

Đối với ksh, zshbash, một cách tiếp cận tiềm năng tốt hơn có thể là:

if ((${#var[@]})); then
  echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi

Đối với ksh93, zshbash4.4 trở lên, cũng có:

if typeset -p var 2> /dev/null | grep -q '^'; then
  echo '$var exists'
fi

Mà sẽ báo cáo các biến đã được thiết lập hoặc khai báo.


1
declare -p/ typeset -plàm việc trong bashbây giờ quá.
cas

1
@cas, không. Không khai báo nhưng không đặt biến. Hãy thử bash -c 'typeset -i a; typeset -p a'và so sánh với ksh93hoặc zsh.
Stéphane Chazelas

+1 Cảm ơn đã chỉ cho tôi ở đây. Cũng xem câu hỏi của tôi unix.stackexchange.com/q/280893/674
Tim

@cas, đã thay đổi với bash-4.4 (phát hành vào tháng 9 năm 2016), tôi đã chỉnh sửa nó trong.
Stéphane Chazelas

9

Như đã đề cập trong câu trả lời trên SO , đây là một cách để kiểm tra:

if [ -z ${somevar+x} ]; then echo "somevar is unset"; else echo "somevar is set to '$somevar'"; fi

trong đó $ {somevar + x} là một khai triển tham số ước tính thành null nếu var không được đặt và thay thế chuỗi "x" bằng cách khác.

Sử dụng -n, như được đề xuất bởi câu trả lời khác, sẽ chỉ kiểm tra xem biến có chứa chuỗi rỗng không. Nó sẽ không kiểm tra sự tồn tại của nó.


2
Bạn cần báo giá $somevarđể xử lý IFS=x. Hoặc là hoặc trích dẫn x.
mikeerv

1
@mikeerv Cảm ơn, tôi thích tìm hiểu về các trường hợp cạnh :) Ý bạn là if [ -z "${somevar+x}" ]gì? Các trích dẫn vẫn sẽ được yêu cầu bên trong [[]]?
Tom Hale

@TomHale - vâng, trong những trường hợp hiếm hoi. các [ testthường trình chấp nhận các tham số dòng lệnh, và do đó, các mở rộng và diễn giải thông thường theo thứ tự theo cách thông thường nên được dựa vào để hiển thị khi gọi thử nghiệm mà bạn nên đọc để đọc qua bất kỳ dòng lệnh chương trình nào. kiểm tra {! + "!"}
mikeerv

@mikeerv Tôi đoán bạn đồng ý với câu hỏi thứ 2 của tôi ... Đây có phải là câu hỏi đầu tiên không?
Tom Hale

1
+1; đây dường như là cách đơn giản nhất để thực hiện việc này khi set -ucó hiệu lực và phiên bản Bash là tiền 4.2.
Kyle Strand

4

POSIXly:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

hoặc bạn có thể để vỏ của bạn hiển thị thông báo cho bạn:

(: "${somevar?}")
zsh: somevar: parameter not set

@mikeerv: Vâng, tất nhiên, có nhiều cách để làm điều đó. Đầu tiên, tôi nghĩ rằng tôi sẽ nhân đôi nó với điều này . Nhưng trong câu hỏi này, OP chỉ muốn kiểm tra, anh ta đã không tuyên bố rằng anh ta muốn thoát hoặc báo cáo nếu không đặt biến, vì vậy tôi đã đi kèm với một kiểm tra trong subshell.
cuonglm

2
Chà, tôi biết, nhưng chính xác là bởi vì bạn phải thực hiện nó trong một khung con như thế cho thấy nó có thể không phải là cách tốt nhất để kiểm tra ở đây - đó là một hành động tạm dừng đối với thất bại, nó không cho phép bất kỳ phương tiện đơn giản nào để xử lý một thất bại. Ngay cả một trapchỉ có thể làm việc trên EXIT. Đó là tất cả những gì tôi đang nói - nó chỉ không áp dụng như một lần vượt qua / thất bại rất tốt. Và đây cũng không phải là tôi nói - tôi đã thực hiện chính xác điều này trước đây và phải mất một cuộc trò chuyện bình luận giống như thế này để thuyết phục tôi. Vì vậy, tôi chỉ nghĩ rằng tôi sẽ trả nó về phía trước.
mikeerv

1
@mikeerv: Chà, tốt, đó là một điểm tốt. Tôi chỉ tự hỏi, việc thêm vào có thể khiến OP nhầm lẫn với cú pháp :)
cuonglm

2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi

Tôi sẽ tưởng tượng điều này là hơi kém hiệu quả, nhưng nó rất đơn giản và shtương thích, đó là những gì tôi cần.
hoijui

2
printf ${var+'$var exists!\n'}

... Sẽ không in gì cả khi nó không. Hoặc là...

printf $"var does%${var+.}s exist%c\n" \ not !

... Sẽ cho bạn biết một trong hai cách.

bạn có thể sử dụng giá trị trả về của kiểm tra để tự động mở rộng sang chuỗi định dạng phù hợp với điều kiện của bạn:

[ "${var+1}" ]
printf $"var does%.$?0s exist%c\n" \ not !

Bạn cũng có thể printfthất bại dựa trên sự thay thế ...

printf $"var does%${var+.}s exist%c\n%.${var+b}d" \
        \ not ! \\c >&"$((2${var+-1}))" 2>/dev/null

... $var does not exist!In ra stderr và trả về 0 khác khi $varkhông được đặt, nhưng in $var does exist!ra thiết bị xuất chuẩn và trả về 0 khi $varđược đặt.


1

Dòng đơn giản này hoạt động (và hoạt động trên hầu hết các vỏ POSIX):

${var+"false"} && echo "var is unset"

Hoặc, được viết dưới dạng dài hơn:

unset var

if ${var+"false"}
then
   echo "var is unset"
fi

Việc mở rộng là:

  • Nếu var có giá trị (thậm chí null), false sẽ được thay thế
  • Nếu var có "không có giá trị" thì "không có giá trị" (null) được thay thế.

Việc ${var+"false"}mở rộng mở rộng thành "null" của "false".
Sau đó, "nothing" hoặc "false" được thực thi và mã thoát được đặt.

Không cần phải gọi lệnh test( [hoặc [[) vì giá trị thoát được đặt bởi (thực thi) chính việc mở rộng.


Có, ngoại trừ trong một số phiên bản cũ của zsh trong mô phỏng sh khi $IFSchứa f, a, l, s hoặc e. Giống như các câu trả lời khác, có trường hợp mảng, giá trị băm hoặc các loại biến khác mà người ta có thể muốn đề cập.
Stéphane Chazelas

@ StéphaneChazelas tôi đã viết most POSIX shells. most có nghĩa làIn the greatest number of instances , không phải tất cả. ... ... Vì vậy, vâng, trong một điều kiện tối nghĩa when $IFS contains f, a, l, s or evà đối với một số vỏ tối nghĩa, some old versions of zshđiều này không thành công: Thật là một cú sốc!. Tôi nên cho rằng lỗi như vậy đã được giải quyết từ lâu. ... ... Bạn đang đề xuất rằng chúng ta phải viết mã cho các shell bị hỏng từ lâu?.

@ StéphaneChazelas Ngoài ra: Tiêu đề câu hỏi rất cụ thể: Bash.

ngựa vằn nhị phân ???
mikeerv

0

Cách vỏ tinh khiết:

[ "${var+1}" ] || echo "The variable has not been set"

Kịch bản thử nghiệm:

#!/bin/sh
echo "Test 1, var has not yet been created"
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 2, var=1"
var=1
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 3, var="
var=
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 4, unset var"
unset var
[ "${var+1}" ] || echo "The variable has not been set"
echo "Done"

Các kết quả:

Test 1, var has not yet been created
The variable has not been set
Test 2, var=1
Test 3, var=
Test 4, unset var
The variable has not been set
Done

1
Thất bại khi biến được đặt thành chuỗi null.
Tom Hale

0

Với bash 4.4.19 sau đây làm việc cho tôi. Đây là một ví dụ đầy đủ

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi

-1

Bạn không thể sử dụng iflệnh để kiểm tra sự tồn tại của các biến được khai báo trong bash tuy nhiên -vtùy chọn tồn tại trong bash mới hơn, nhưng nó không khả dụng và bạn không thể sử dụng nó trong các bashphiên bản cũ hơn . Bởi vì khi bạn đang sử dụng một biến nếu nó không tồn tại thì nó sẽ sinh ra cùng một lúc.

Ví dụ: Hãy tưởng tượng rằng tôi không sử dụng hoặc gán giá trị cho MYTESTbiến, nhưng khi bạn đang sử dụng lệnh echo, nó không hiển thị gì cho bạn! Hoặc nếu bạn đang sử dụng if [ -z $MYTEST ]nó trả về giá trị bằng không! Nó không trả về trạng thái thoát khác, cho bạn biết rằng biến này không tồn tại!

Bây giờ bạn có hai giải pháp (Không có -vtùy chọn):

  1. Sử dụng declarelệnh.
  2. Sử dụng setlệnh.

Ví dụ:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

Nhưng thật không may, các lệnh này cho thấy bạn đã tải các chức năng trong bộ nhớ! Bạn có thể sử dụng declare -p | grep -q MYTEST ; echo $?lệnh cho kết quả sạch hơn.


-1

Chức năng kiểm tra nếu biến được khai báo / hủy đặt

bao gồm cả trống $array=()


Ngoài câu trả lời của @ Gilles

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

- mà tôi không tìm được cách gói gọn nó trong một hàm - Tôi muốn thêm một phiên bản đơn giản, một phần dựa trên câu trả lời của Richard Hansen , nhưng cũng giải quyết được cạm bẫy xảy ra với một khoảng trống :array=()

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • Bằng cách kiểm tra đầu tiên nếu biến (un) được đặt, có thể tránh lệnh gọi khai báo, nếu không cần thiết.
  • Tuy nhiên, nếu $1chứa tên trống $array=(), lệnh gọi khai báo sẽ đảm bảo chúng tôi nhận được kết quả đúng
  • Không bao giờ có nhiều dữ liệu được truyền đến / dev / null vì khai báo chỉ được gọi nếu biến không được đặt hoặc một mảng trống.


Với đoạn mã sau, các chức năng có thể được kiểm tra:

( # start a subshell to encapsulate functions/vars for easy copy-paste into the terminal
  # do not use this extra parenthesis () in a script!

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}

:;       echo -n 'a;       '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=;      echo -n 'a=;      '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a="sd";  echo -n 'a="sd";  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=();    echo -n 'a=();    '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=("");  echo -n 'a=("");  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
unset a; echo -n 'unset a; '; var_is_declared a && echo "# is declared" || echo "# is not declared"
echo ;
:;       echo -n 'a;       '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=;      echo -n 'a=;      '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a="foo"; echo -n 'a="foo"; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=();    echo -n 'a=();    '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=("");  echo -n 'a=("");  '; var_is_unset a && echo "# is unset" || echo "# is not unset"
unset a; echo -n 'unset a; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
)

Kịch bản sẽ trả về

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset

-1

Hàm bash hoạt động cho cả hai kiểu vô hướngmảng :

Định nghĩa

has_declare() { # check if variable is set at all
    local "$@" # inject 'name' argument in local scope
    &>/dev/null declare -p "$name" # return 0 when var is present
}

cầu khẩn

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
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.