shell bash变量赋值 - 如何检查变量是否在Bash中设置?




usage用法 bash教程 (25)

要检查变量是否设置为非空值,请使用[ -n "$x" ] ,如其他人已经指出的那样。

大多数情况下,最好是将未赋值的变量与空值相同的变量进行处理。 但是,如果您需要: [ -n "${x+set}" ]"${x+set}"扩展为set x设置,如果x未设置,则设为空字符串)。

要检查一个参数是否已经通过,请测试$# ,这是传递给函数的参数的数量(或不在函数中的脚本)(见Paul的答案 )。

我如何知道是否在Bash中设置了一个变量?

例如,如何检查用户是否将第一个参数提供给函数?

function a {
    # if $1 is set ?
}

我总是发现其他答案中的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"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

请注意,每个组(具有和不具有前面的冒号)具有相同的设置和未设置的情况,所以唯一不同的是处理空白情况的方式。

使用前面的冒号, 空的和未设置的情况是相同的,所以我会尽可能使用那些(即使用:= ,而不仅仅是= ,因为空的情况不一致)。

标题:

  • 设置意味着VARIABLE非空( VARIABLE="something"
  • 表示VARIABLE为空/空( VARIABLE=""
  • 未设置表示VARIABLE不存在(未unset VARIABLE

价值观:

  • $VARIABLE表示结果是变量的原始值。
  • "default"表示结果是提供的替换字符串。
  • ""表示结果为空(一个空字符串)。
  • exit 127表示脚本停止执行,退出代码为127。
  • $(VARIABLE="default")表示结果是变量的原始值, 并且提供的替换字符串被分配给该变量以备将来使用。

要检查非空/非零字符串变量,即如果设置,请使用

if [ -n "$1" ]

这与-z相反。 我发现自己使用-n多于-z


虽然这里陈述的大多数技术都是正确的,但bash 4.2支持对变量( man bash )的存在进行实际测试,而不是测试变量的值。

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

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

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

在bash中,你可以在[[ ]]内部使用-v

#! /bin/bash -u

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

echo $SOMEVAR

阅读bash手册页的“参数扩展”部分。 参数扩展不会为正在设置的变量提供通用测试,但如果未设置参数,则可以对参数执行几个操作。

例如:

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

first_arg设置first_arg等于$1如果它被分配,否则它使用值“foo”。 如果绝对必须采用单个参数,并且不存在良好的默认值,则可以在未给出参数时退出并显示错误消息:

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

(注意使用:作为一个空命令,它只是扩展了它的参数的值,我们不想在这个例子中用$1做任何事情,如果没有设置,就退出)


正确的方式

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 unset"; else echo "var is set to '$var'"; fi

这是因为它不区分未设置的变量和设置为空字符串的变量。 也就是说,如果var='' ,那么上述解决方案将错误地输出该var未设置。

但是这种区别在用户必须指定扩展名或附加属性列表并且未指定它们的情况下是非常重要的,它们默认为非空值,而指定空字符串应该使脚本使用空的扩展名或列表的附加属性。


为了查看一个变量是否为非空,我使用

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

对于那些在set -u的脚本中查找未设置或空的时候:

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

常规的[ -z "$var" ]检查将失败, var; unbound variable 如果set -u则为非var; unbound variable ,但如果var var; unbound variable set -u var; unbound variable set -u ,则[ -z "${var-}" ] expands为空字符串。


当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”模式下定义


以下是如何测试参数是未设置还是为空(“空”)设置值

+--------------------+----------------------+-----------------+-----------------+
|                    |       parameter      |     parameter   |    parameter    |
|                    |   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:参数扩展

在用“替代”显示的所有情况下,表达式都会替换为显示的值。 在使用“分配”显示的所有情况下,参数都会分配该值,该值也会替换表达式。


你可以做:

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

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

如果var可以是数组,则[ -z "${var+x}" ]参数替换不正确。 要确定在Bash中你需要使用[ "${#var[@]}" = 0 ]这样的数组语法 ,如下所示。

is-var-set () {
    results="\${var+x}=${var+x}\t\${#var[@]}=${#var[@]}"
    if [ -z "${var+x}" ] && [ "${#var[@]}" = 0 ]; then
        echo -e "$1: var's unset.\t$results"
    elif [ -n "${var+x}" ] && [ "${#var[@]}" != 0 ]; then
        echo -e "$1: var is set. \t$results"
    else
        echo -e "$1: Is var set? \t$results"
    fi
    unset var # so we don't have to do it everywhere else
}

几乎在所有情况下,他们都同意。 我发现数组方法更精确的唯一情况是变量是位置为0的非空数组(例如,在下面的测试7A中)。 这种不一致来自于$var它是${var[0]}缩写,所以[ -z "${var+x}" ]不检查整个数组。

这里是我的测试用例。

unset var;      is-var-set 1 # var unset
var='';         is-var-set 2 # var[0] set to ''
var=foo;        is-var-set 3 # var[0] set to 'foo'
var=();         is-var-set 4 # var unset (all indices)
var=(foo);      is-var-set 5 # var[0] set to 'foo'
var=([0]=foo);  is-var-set 6 # var[0] set to 'foo'
var=([1]=foo);  is-var-set 7 # var[0] unset, but var[1] set to 'foo'
declare -a var; is-var-set 8 # var empty, but declared as an array
declare -A var; is-var-set 9 # var empty, but declared as an associative array
declare -A var  # Because is-var-set() conveniently unsets it
var=([xz]=foo); is-var-set A # var[xz] set to 'foo', but var's otherwise empty
declare -a var  # Demonstrate that Bash knows about var, even when there's
declare -A var; is-var-set B # apparently no way to just _check_ its existence

这是输出。

1: var's unset. ${var+x}=       ${#var[@]}=0
2: var is set.  ${var+x}=x      ${#var[@]}=1
3: var is set.  ${var+x}=x      ${#var[@]}=1
4: var's unset. ${var+x}=       ${#var[@]}=0
5: var is set.  ${var+x}=x      ${#var[@]}=1
6: var is set.  ${var+x}=x      ${#var[@]}=1
7: Is var set?  ${var+x}=       ${#var[@]}=1
8: var's unset. ${var+x}=       ${#var[@]}=0
9: var's unset. ${var+x}=       ${#var[@]}=0
A: Is var set?  ${var+x}=       ${#var[@]}=1
./foo.sh: line 26: declare: var: cannot convert indexed to associative array
B: var's unset. ${var+x}=       ${#var[@]}=0

总共:

  • 在大多数情况下, ${var+x}参数扩展语法的工作原理与${#var[@]}数组语法一样,例如检查函数的参数。 这种情况可能会破坏的唯一方式是如果未来版本的Bash添加了一种将数组传递给函数而不将内容转换为单个参数的方法。
  • 数组语法对于元素0设置的非空数组(需要关联或不需要)是必需的。
  • Neither syntax explains what's going on if declare -a var has been used without assigning even a null value somewhere in the array. Bash still distinguishes the case somewhere (as seen in test B above), so this answer's not foolproof. Fortunately Bash converts exported environment variables into strings when running a program/script, so any issues with declared-but-unset variables will be contained to a single script, at least if it's not sourcing other scripts.

在shell中,可以使用-z运算符,如果字符串的长度为零,则该运算符为True。

如果未设置默认MY_VAR ,可以使用简单的单行设置,否则可以选择显示消息:

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

I like auxiliary functions to hide the crude details of bash. In this case, doing so adds even more (hidden) crudeness:

# 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} ]
}

Because I first had bugs in this implementation (inspired by the answers of Jens and Lionel), I came up with a different solution:

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

I find it to be more straight-forward, more bashy and easier to understand/remember. Test case shows it is equivalent:

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

The test case also shows that local var does NOT declare var (unless followed by '='). For quite some time I thought i declared variables this way, just to discover now that i merely expressed my intention... It's a no-op, i guess.

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

BONUS: usecase

I mostly use this test to give (and return) parameters to functions in a somewhat "elegant" and safe way (almost resembling an interface...):

#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

If called with all requires variables declared:

Hello world!

其他:

Error: variable not declared: outputStr


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

如果你想检查[email protected]任何内容,我发现了一个(更好)更好的代码。

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

为什么这一切? [email protected]所有内容都存在于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

[[ $foo ]]

要么

(( ${#foo} ))

要么

let ${#foo}

要么

declare -p foo

使用[[ -z "$var" ]]是知道变量是否被设置的最简单方法,但该选项不区分未设置的变量和设置为空字符串的变量:

 set=''

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

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

最好根据变量类型来检查它:env变量,参数或常规变量。

对于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

   return $?
}

declare_var "var_name" && echo "Set" || echo "Unset"

笔记:

$#                  says you the number of positional parameters.

declare -p          gives you the definition of the variable passed as parameter. If it 
                    exists, returns 0, if not, returns 1 and prints an error message.

$?                  gives you the status code of the last executed command.

这就是我每天都在使用的东西:

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

这在Linux和Solaris下运行良好,最终崩溃到3.0。

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

我总是使用这个,基于这样一个事实:任何人第一次看到代码似乎都很容易理解:

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

而且,如果想检查是否不为空;

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

而已。


If you wish to test that a variable is bound or unbound, this works well, even after you've turned on the nounset option:

set -o noun set

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

Okay guys!

Here's my answer!

DELIMITER_VAL='='

read -d '' F_ABOUT_DISTRO_R <<"EOF"
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
NAME="Ubuntu"
VERSION="14.04.4 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.4 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
EOF

SPLIT_NOW=$(awk -F$DELIMITER_VAL '{for(i=1;i<=NF;i++){printf "%s\n", $i}}' <<<"${F_ABOUT_DISTRO_R}")
while read -r line; do
   SPLIT+=("$line")
done <<< "$SPLIT_NOW"
for i in "${SPLIT[@]}"; do
    echo "$i"
done

Why this approach is "the best" for me?

Because of two reasons:

  1. You do not need to escape the delimiter;
  2. You will not have problem with blank spaces . The value will be properly separated in the array!

[]'s







bash shell variables