簡體   English   中英

"如何檢查是否在 Bash 中設置了變量?"

[英]How to check if a variable is set in Bash?

如何知道 Bash 中是否設置了變量?

例如,如何檢查用戶是否將第一個參數提供給函數?

function a {
    # if $1 is set ?
}

(通常)正確的方法

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

其中${var+x}是一個參數擴展,如果var未設置,則計算結果為零,否則替換字符串x

行情題外話

引號可以省略(所以我們可以說${var+x}而不是"${var+x}" )因為這種語法和用法保證這只會擴展到不需要引號的東西(因為它要么擴展到x (它不包含分詞符,因此不需要引號),或者什么也不做(這會導致[ -z ] ,它方便地評估為與[ -z "" ]相同的值(真)))。

然而,雖然可以安全地省略引號,並且對所有人來說並不是立即顯而易見的(對於也是主要 Bash 編碼器的此引號解釋的第一作者來說甚至不明顯),有時編寫解決方案會更好引用為[ -z "${var+x}" ] ,以 O(1) 速度損失的非常小的可能成本。 第一作者還在使用此解決方案的代碼旁邊添加了此注釋,給出了此答案的 URL,現在還包括解釋為什么可以安全地省略引號。

(經常)走錯路

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

這通常是錯誤的,因為它不區分未設置的變量和設置為空字符串的變量。 也就是說,如果var='' ,那么上面的解決方案將輸出“var is blank”。

在用戶必須指定擴展名或附加屬性列表的情況下,未設置和“設置為空字符串”之間的區別是必不可少的,並且不指定它們默認為非空值,而指定空字符串應該使腳本使用空擴展名或附加屬性列表。

不過,這種區別可能並非在每種情況下都必不可少。 在這些情況下[ -z "$var" ]會很好。

要檢查非空/非零字符串變量,即如果設置,請使用

if [ -n "$1" ]

它與-z相反。 我發現自己使用-n多於-z

你會像這樣使用它:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

以下是如何測試參數是unset還是空(“Null”)設置了 value

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

來源: POSIX:參數擴展

在所有顯示為“替換”的情況下,表達式將替換為顯示的值。 在所有顯示為“assign”的情況下,參數都被分配了那個值,它也替換了表達式。

要在行動中展示這一點:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

雖然此處所述的大多數技術都是正確的,但bash 4.2支持對變量是否存在的實際測試 ( man bash ),而不是測試變量的值。

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

值得注意的是,這種方法在set -u / set -o nounset模式下用於檢查未設置的變量時不會導致錯誤,這與許多其他方法不同,例如使用[ -z

有很多方法可以做到這一點,以下是其中之一:

if [ -z "$1" ]

如果 $1 為空或未設置,則成功

我總是發現另一個答案中的 POSIX 表很難理解,所以這是我的看法:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

請注意,每個組(有和沒有前面的冒號)具有相同的設置和未設置案例,因此唯一不同的是如何處理案例。

對於前面的冒號,和未設置的情況是相同的,所以我會盡可能使用那些(即使用:= ,而不僅僅是= ,因為空情況不一致)。

標題:

  • set意味着VARIABLE是非空的( VARIABLE="something"
  • 表示VARIABLE為空/空( VARIABLE=""
  • unset意味着VARIABLE不存在( unset VARIABLE

價值觀:

  • $VARIABLE表示結果是變量的原始值。
  • "default"表示結果是提供的替換字符串。
  • ""表示結果為空(空字符串)。
  • exit 127表示腳本停止執行,退出代碼為 127。
  • $(VARIABLE="default")表示結果是"default"並且VARIABLE (以前為空或未設置)也將設置為等於"default"

要查看變量是否為非空,我使用

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

相反的測試變量是否未設置或為空:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

要查看是否設置了變量(空或非空),我使用

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

相反的測試是否未設置變量:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

在現代版本的 Bash 上(我認為是 4.2 或更高版本;我不確定),我會試試這個:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

筆記

由於bash標簽,我給出了一個以 Bash 為重點的答案。

簡答

只要你在 Bash 中只處理命名變量,這個函數應該總是告訴你變量是否已經設置,即使它是一個空數組。

variable-is-set() {
    declare -p "$1" &>/dev/null
}

為什么這有效

在 Bash 中(至少可以追溯到 3.0),如果var是一個聲明/設置變量,那么declare -p var輸出一個declare命令,該命令將變量var設置為其當前類型和值,並返回狀態代碼0 (成功)。 如果var未聲明,則declare -p var將錯誤消息輸出到stderr並返回狀態代碼1 使用&>/dev/null ,將常規stdoutstderr輸出重定向到/dev/null ,永遠不會被看到,並且不會更改狀態代碼。 因此該函數只返回狀態代碼。

為什么其他方法(有時)在 Bash 中失敗

  • [ -n "$var" ]這僅檢查${var[0]}是否為非空。 (在 Bash 中, $var${var[0]} 。)
  • [ -n "${var+x}" ]這僅檢查是否設置了${var[0]}
  • [ "${#var[@]}" != 0 ]這只檢查是否至少設置了$var一個索引。

當此方法在 Bash 中失敗時

這僅適用於命名變量(包括$_ ),不適用於某些特殊變量( $!$@$#$$$*$?$-$0$1$2 、...,以及任何我可能忘記了)。 由於這些都不是數組,POSIX 風格的[ -n "${var+x}" ]適用於所有這些特殊變量。 但要注意將其包裝在函數中,因為在調用函數時許多特殊變量會改變值/存在。

外殼兼容性說明

如果您的腳本具有數組並且您試圖使其與盡可能多的 shell 兼容,那么請考慮使用typeset -p而不是declare -p 我讀過 ksh 只支持前者,但無法對此進行測試。 我確實知道 Bash 3.0+ 和 Zsh 5.5.1 都支持typeset -pdeclare -p ,區別僅在於哪一個是另一個的替代方案。 但是除了這兩個關鍵字之外,我還沒有測試過差異,也沒有測試過其他 shell。

如果您需要腳本與 POSIX sh 兼容,則不能使用數組。 沒有數組, [ -n "{$var+x}" ]有效。

Bash 中不同方法的比較代碼

此函數取消設置變量vareval是傳遞的代碼,運行測試以確定var是否由eval d 代碼設置,最后顯示不同測試的結果狀態代碼。

我正在跳過test -v var[ -v var ][[ -v var ]]因為它們產生與 POSIX 標准相同的結果[ -n "${var+x}" ] ,同時需要 Bash 4.2+ . 我還跳過了typeset -p因為它與我測試過的 shell(Bash 3.0 到 5.0 和 Zsh 5.5.1)中的declare -p相同。

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
}

測試用例代碼

請注意,如果變量未聲明為關聯數組,則由於 Bash 將非數字數組索引視為“0”,因此測試結果可能出乎意料。 此外,關聯數組僅在 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

測試輸出

標題行中的測試助記符對應於[ -n "$var" ][ -n "${var+x}" ][ "${#var[@]}" != 0 ] ,並declare -p var ,分別。

                         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

概括

  • declare -p var &>/dev/null對於在 Bash 中測試命名變量是(100%?)可靠的,至少從 3.0.
  • [ -n "${var+x}" ]在符合 POSIX 的情況下是可靠的,但不能處理數組。
  • 其他測試用於檢查變量是否為非空,以及用於檢查其他 shell 中聲明的變量。 但是這些測試既不適合 Bash 也不適合 POSIX 腳本。
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

盡管對於參數,我認為通常最好測試 $#,即參數的數量。

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

如果未設置,您想退出

這對我有用。 如果未設置參數,我希望我的腳本退出並顯示錯誤消息。

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

這在運行時返回錯誤

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

僅檢查,不退出 - Empty 和 Unset 無效

如果您只想檢查值是 set=VALID 還是 unset/empty=INVALID,請嘗試使用此選項。

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

或者,即使是簡短的測試 ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

只檢查,不退出 - 只有空是無效的

這就是問題的答案。 如果您只想檢查值是 set/empty=VALID 還是 unset=INVALID,請使用此選項。

注意,“..-1}”中的“1”是無關緊要的,它可以是任何東西(比如 x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

簡短測試

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

我將此答案獻給@mklement0(評論),他要求我准確回答問題。

參考http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02

對於那些希望在帶有set -u的腳本中檢查 unset或 empty 的人

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

常規的[ -z "$var" ]檢查將因var; unbound variable失敗var; unbound variable var; unbound variable如果set -u[ -z "${var-}" ]如果未設置var擴展為空字符串而不會失敗。

閱讀bash手冊頁的“參數擴展”部分。 參數擴展不提供對正在設置的變量的一般測試,但是如果未設置參數,您可以對它執行幾項操作。

例如:

function a {
    first_arg=${1-foo}
    # rest of the function
}

如果已賦值,則將first_arg設置first_arg等於$1 ,否則將使用值“foo”。 如果a絕對必須采用一個參數,並沒有很好的默認存在,你可以沒有給出參數時出現錯誤消息退出:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(注意使用:作為空命令,它只是擴展其參數的值。在這個例子中我們不想對$1做任何事情,如果沒有設置就退出)

要檢查變量是否設置為非空值,請使用[ -n "$x" ] ,正如其他人已經指出的那樣。

大多數情況下,以與未設置的變量相同的方式處理具有空值的變量是個好主意。 但是你可以區分這兩種,如果你需要: [ -n "${x+set}" ] "${x+set}"擴展為set ,如果x被設置,為空字符串,如果x是未設置)。

要檢查是否已傳遞參數,請測試$# ,它是傳遞給函數(或在不在函數中時傳遞給腳本)的參數數量(請參閱Paul 的回答)。

在 bash 中,您可以在[[ ]]內置函數中使用-v

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

概括

  • 使用test -n "${var-}"檢查變量是否為空(因此也必須定義/設置)。 用法:

     if test -n "${name-}"; then echo "name is set to $name" else echo "name is not set or empty" fi
  • 使用test ! -z "${var+}" test ! -z "${var+}"來檢查變量是否被定義/設置(即使它是空的)。 用法:

     if test ! -z "${var+}"; then echo "name is set to $name" else echo "name is not set" fi

請注意,第一個用例在 shell 腳本中更為常見,這也是您通常想要使用的。

筆記

  • 此解決方案應該適用於所有 POSIX shell(sh、bash、zsh、ksh、dash)
  • 這個問題的其他一些答案是正確的,但可能會讓沒有 shell 腳本經驗的人感到困惑,所以我想提供一個 TLDR 答案,這樣的人不會感到困惑。

解釋

要了解此解決方案的工作原理,您需要了解POSIX test命令和 POSIX shell 參數擴展 ( spec ),因此讓我們介紹了解答案所需的絕對基礎知識。

test 命令評估表達式並返回 true 或 false(通過其退出狀態)。 如果操作數是非空字符串,則運算符-n返回 true。 例如, test -n "a"返回 true,而test -n ""返回 false。 現在,要檢查變量是否為空(這意味着它必須被定義),您可以使用test -n "$var" 但是,一些 shell 腳本有一個選項集 ( set -u ),它會導致對未定義變量的任何引用發出錯誤,因此如果變量var未定義,則表達式$a將導致錯誤。 要正確處理這種情況,您必須使用變量擴展,如果變量未定義,它將告訴 shell 用替代字符串替換變量,避免上述錯誤。

變量擴展${var-}意味着:如果變量var未定義(也稱為“unset”),則將其替換為空字符串。 因此,如果$var不為空, test -n "${var-}"將返回 true,這幾乎總是您要在 shell 腳本中檢查的內容。 如果$var未定義或不為空,則反向檢查將是test -z "${var-}"

現在到第二個用例:檢查變量var是否已定義,無論是否為空。 這是一個不太常見的用例,稍微復雜一些,我建議您閱讀Lionels 的精彩答案以更好地理解它。

使用[[ -z "$var" ]]是了解變量是否已設置的最簡單方法,但該選項-z不區分未設置的變量和設置為空字符串的變量:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

最好根據變量的類型進行檢查:env變量、參數或常規變量。

對於環境變量:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

對於參數(例如,檢查參數$5是否存在):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

對於常規變量(使用輔助函數,以優雅的方式完成):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

筆記:

  • $#給你位置參數的數量。
  • declare -p為您提供作為參數傳遞的變量的定義。 如果存在,返回 0,如果不存在,返回 1 並打印錯誤信息。
  • &> /dev/null抑制來自declare -p輸出而不影響其返回代碼。

我很驚訝沒有人試圖編寫一個 shell 腳本來以編程方式生成臭名昭著的難以理解的 table 既然我們在這里學習編碼技術,為什么不用代碼表達答案呢? :) 這是我的看法(應該適用於任何 POSIX shell):

H="+-%s-+-%s----+-%s----+-%s--+\n"       # table divider printf format
R="| %-10s | %-10s | %-10s | %-10s |\n"  # table row printf format

S='V'     # S is a variable that is set-and-not-null
N=''      # N is a variable that is set-but-null (empty "")
unset U   # U is a variable that is unset

printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "expression" "FOO='V'" "FOO='' " "unset FOO";
printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "\${FOO:-x}" "${S:-x}" "${N:-x}" "${U:-x}  "; S='V';N='';unset U
printf "$R" "\${FOO-x} " "${S-x} " "${N-x} " "${U-x}   "; S='V';N='';unset U
printf "$R" "\${FOO:=x}" "${S:=x}" "${N:=x}" "${U:=x}  "; S='V';N='';unset U
printf "$R" "\${FOO=x} " "${S=x} " "${N=x} " "${U=x}   "; S='V';N='';unset U
printf "$R" "\${FOO:?x}" "${S:?x}" "<error>" "<error>  "; S='V';N='';unset U
printf "$R" "\${FOO?x} " "${S?x} " "${N?x} " "<error>  "; S='V';N='';unset U
printf "$R" "\${FOO:+x}" "${S:+x}" "${N:+x}" "${U:+x}  "; S='V';N='';unset U
printf "$R" "\${FOO+x} " "${S+x} " "${N+x} " "${U+x}   "; S='V';N='';unset U
printf "$H" "----------" "-------" "-------" "---------";

# For reference, the following two lines are the literal rows of the table
# which would cause the script to exit and so have had <error> written in.
#printf "$R" "\${FOO:?x}" "${S:?x}" "${N:?x}" "${U:?x}  "; S='V';N='';unset U
#printf "$R" "\${FOO?x} " "${S?x} " "${N?x} " "${U?x}   "; S='V';N='';unset U

以及運行腳本的輸出:

+------------+------------+------------+------------+
| expression | FOO='V'    | FOO=''     | unset FOO  |
+------------+------------+------------+------------+
| ${FOO:-x}  | V          | x          | x          |
| ${FOO-x}   | V          |            | x          |
| ${FOO:=x}  | V          | x          | x          |
| ${FOO=x}   | V          |            | x          |
| ${FOO:?x}  | V          | <error>    | <error>    |
| ${FOO?x}   | V          |            | <error>    |
| ${FOO:+x}  | x          |            |            |
| ${FOO+x}   | x          | x          |            |
+------------+------------+------------+------------+

該腳本缺少一些功能,例如在副作用分配發生(或不發生)發生時顯示,但也許其他一些更有野心的人想要從這個起點開始運行。

為了清楚地回答 OP 關於如何確定是否設置變量的問題,@Lionel 的回答是正確的:

if test "${name+x}"; then
    echo 'name is set'
else
    echo 'name is not set'
fi

這個問題已經有很多答案,但沒有一個提供真正的布爾表達式來清楚地區分變量值。

以下是我想出的一些明確的表達方式:

+-----------------------+-------------+---------+------------+
| Expression in script  | name='fish' | name='' | unset name |
+-----------------------+-------------+---------+------------+
| test "$name"          | TRUE        | f       | f          |
| test -n "$name"       | TRUE        | f       | f          |
| test ! -z "$name"     | TRUE        | f       | f          |
| test ! "${name-x}"    | f           | TRUE    | f          |
| test ! "${name+x}"    | f           | f       | TRUE       |
+-----------------------+-------------+---------+------------+

順便說一下,這些表達式是等價的: test <expression> <=> [ <expression> ]

其他需要謹慎使用的歧義表達:

+----------------------+-------------+---------+------------+
| Expression in script | name='fish' | name='' | unset name |
+----------------------+-------------+---------+------------+
| test "${name+x}"     | TRUE        | TRUE    | f          |
| test "${name-x}"     | TRUE        | f       | TRUE       |
| test -z "$name"      | f           | TRUE    | TRUE       |
| test ! "$name"       | f           | TRUE    | TRUE       |
| test ! -n "$name"    | f           | TRUE    | TRUE       |
| test "$name" = ''    | f           | TRUE    | TRUE       |
+----------------------+-------------+---------+------------+

啟用 Bash 選項set -u時,上述答案不起作用。 此外,它們不是動態的,例如,如何測試定義了名稱為“dummy”的變量? 嘗試這個:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

相關:在 Bash 中,如何測試變量是否以“-u”模式定義

你可以做:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}

我的首選方式是這樣的:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

所以基本上,如果一個變量被設置,它就會變成“對結果false的否定”(什么將是true =“設置”)。

並且,如果它未設置,它將成為“對結果為true否定”(因為空結果評估為true )(因此將以false = "NOT set" 結束)。

這是我每天使用的:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set() { [[ $(eval echo "\${${1}+x}") ]]; }

這在 Linux 和 Solaris 下運行良好,直至 bash 3.0。

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ myvar=
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

測試是否設置了變量var[ ${var+x} ]

要測試變量是否按名稱設置: [ ${!name+x} ]

要測試是否設置了位置參數: [ ${N+x} ] ,其中 N 實際上是一個整數。

這個答案幾乎與 Lionel 的相似,但通過省略-z探索更簡約的風格。

要測試是否設置了命名變量:

function is_set {
    local v=$1
    echo -n "${v}"
    if [ ${!v+x} ]; then
        echo " = '${!v}'"
    else
        echo " is unset"
    fi
}

要測試是否設置了位置參數:

function a {
    if [ ${1+x} ]; then
        local arg=$1
        echo "a '${arg}'"
    else
        echo "a: arg is unset"
    fi
}

測試表明不需要額外注意空格和有效的測試表達式。

set -eu

V1=a
V2=
V4=-gt
V5="1 -gt 2"
V6="! -z 1"
V7='$(exit 1)'

is_set V1
is_set V2
is_set V3
is_set V4
is_set V5
is_set V6
is_set V7

a 1
a
a "1 -gt 2"
a 1 -gt 2
$./test.sh 
V1 = 'a'
V2 = ''
V3 is unset
V4 = '-gt'
V5 = '1 -gt 2'
V6 = '! -z 1'
V7 = '$(exit 1)'
a '1'
a: arg is unset
a '1 -gt 2'
a '1'

最后,注意set -eu可以保護我們免受常見錯誤的影響,例如變量名稱中的拼寫錯誤。 我推薦它的用法,但這意味着未設置變量和使用空字符串設置的變量之間的差異得到了正確處理。

在 shell 中,您可以使用-z運算符,如果字符串的長度為零,則該運算符為 True。

如果未設置,則設置默認MY_VAR簡單MY_VAR ,否則您可以選擇顯示消息:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
[[ $foo ]]

或者

(( ${#foo} ))

或者

let ${#foo}

或者

declare -p foo
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

我總是使用這一代碼,是基於這樣的事實,即任何首次看到該代碼的人都容易理解:

if [ "$variable" = "" ]
    then
    echo "Variable X is empty"
fi

並且,如果要檢查是否不為空;

if [ ! "$variable" = "" ]
    then
    echo "Variable X is not empty"
fi

而已。

如果您想檢查$@任何內容,我找到了一個(好多)更好的代碼來執行此操作。

if [[ $1 = "" ]]
then
  echo '$1 is blank'
else
  echo '$1 is filled up'
fi

為什么這一切? $@所有內容都存在於 Bash 中,但默認情況下它是空白的,因此test -ztest -n無法幫助您。

更新:您還可以計算參數中的字符數。

if [ ${#1} = 0 ]
then
  echo '$1 is blank'
else
  echo '$1 is filled up'
fi
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

我喜歡輔助函數來隱藏 bash 的粗略細節。 在這種情況下,這樣做會增加更多(隱藏)的粗糙性:

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

因為我首先在這個實現中遇到了錯誤(受到 Jens 和 Lionel 的回答的啟發),我想出了一個不同的解決方案:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

我發現它更直接,更害羞,更容易理解/記住。 測試用例表明它是等效的:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

測試用例還表明, local var沒有聲明VAR(除非后跟“=”)。 很長一段時間以來,我認為我以這種方式聲明了變量,現在只是為了發現我只是表達了我的意圖......我猜這是一個空操作。

IsDeclared_Tricky xyz:1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky 欄:0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared 欄:0
IsDeclared baz: 0

獎勵:用例

我主要使用此測試以某種“優雅”和安全的方式(幾乎類似於接口......)為函數提供(和返回)參數:

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

如果在所有需要聲明的變量的情況下調用:

你好,世界!

別的:

錯誤:變量未聲明:outputStr

我如何知道是否在Bash中設置了變量?

例如,如何檢查用戶是否將第一個參數賦予函數?

function a {
    # if $1 is set ?
}

我如何知道是否在Bash中設置了變量?

例如,如何檢查用戶是否將第一個參數賦予函數?

function a {
    # if $1 is set ?
}

如果您想測試一個變量是否綁定或未綁定,即使您已啟用名詞集選項,該方法也能很好地工作:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

檢查變量是否已聲明/未設置的函數

包括空的$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;} 
}
  • 通過首先測試變量是否已設置,可以避免調用聲明,如果不需要的話。
  • 但是,如果$1包含一個空的$array=() ,則進行聲明的調用將確保我們得到正確的結果
  • 傳遞給/ dev / null的數據很少,因為只有在未設置變量或空數組的情況下才調用聲明。

此功能將在以下情況下進行測試:

 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 

更多細節

和測試腳本看到我的回答這個問題“我如何檢查是否在bash存在的變量?”

備注: Peregring-lk回答也顯示了clarify declare -p的類似用法,這確實是巧合。 否則我當然會相信它!

聲明一個簡單的函數is_set ,它使用declare -p直接測試變量是否存在。

$ is_set() {
    declare -p $1 >/dev/null 2>&1
}

$ is_set foo; echo $?
0

$ declare foo

$ is_set foo; echo $?
1
case "$1" in
 "") echo "blank";;
 *) echo "set"
esac

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM