如何检查“ if”语句中是否存在变量?


69

我需要检查if语句中变量的存在。效果:

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

最接近的问题是this,实际上并不能回答我的问题。


如果你想设置$somevar的值/字符串,如果变量不存在:${somevar:=42}
赛勒斯(Cyrus)2015年

就个人而言,我倾向于只检查是否为空([ -n "$var" ][ ! -z "$var" ])。我认为存在/不存在检查太微妙了,我更喜欢我的代码粗糙而简单。
PSkocik 2015年

Answers:


97

在现代bash中(4.2及更高版本):

[[ -v name_of_var ]]

来自help test

-v VAR,如果设置了shell变量VAR,则为True


3
也可用于单括号:[ -v name_of_var ]
meuh 2015年

7
请注意,对于散列和数组,除非变量具有键/索引“ 0”的元素,否则它将返回false。对于名称引用,它将测试是否定义了目标。它没有特殊的参数一样工作$1$-$#...
斯特凡Chazelas

4
此功能仅在内置的bash test[;中存在。在中不可用/usr/bin/testman test与比较help test
马克·拉卡塔

@MarkLakata是的,因为外部命令无法知道外壳的内部状态。
克里斯·

嗯,不应该总是[[-v“ $ name_of_var”]]吗?
亚历山大·米尔斯

24

取决于你的意思是存在

是否存在已声明但未分配的变量?

是否存在已分配了空列表的数组(或哈希)变量?

做了nameref变量指向一个变量,目前未分配存在

你认为$-$#$1变量?(POSIX没有)。

在类似伯恩的贝壳中,规范的方式是:

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

这适用于标量等参数来判断一个变量被分配一个值(自动清空与否,从环境,assigments, readfor或其他)。

对于有壳typesetdeclare命令,因为这将不报告设置已变量声明,但不分配zsh

对于支持阵列,除了壳yashzsh,不会作为报告设定,除非指数之0的元件已被设定数组变量。

对于bash(但不是ksh93也不zsh),类型变量关联数组,这将不报告它们作为一组,除非他们的密钥已经被设置为“0”的元素。

对于ksh93bash,对于nameref类型的变量,仅当nameref引用的变量本身被视为set时,才返回true 。

对于kshzshbash,潜在的更好的方法可能是:

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

对于ksh93zsh以及bash4.4以上,这里还有:

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

它将报告已设置或声明的变量。


1
declare -p/ typeset -p在工作bash,现在也是。
cas

1
@cas,不。不适用于已声明但未设置的变量。尝试bash -c 'typeset -i a; typeset -p a'ksh93或进行比较zsh
斯特凡Chazelas

+1感谢您在这里指出我。另请参阅我的问题unix.stackexchange.com/q/280893/674
蒂姆

@cas,随bash-4.4(2016年9月发布)而改变,我对此进行了编辑。
StéphaneChazelas 17年

9

如关于SO的答案中所述,这是一种检查方法:

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

其中$ {somevar + x}是参数扩展,如果未设置var,则计算结果为null,否则替换字符串“ x”。

-n如另一个答案所建议的那样,使用,将仅检查变量是否包含空字符串。它不会检查其存在。


2
您需要报价$somevar处理IFS=x。要么引用,要么引用x
mikeserv

1
@mikeserv谢谢,我喜欢学习边缘情况:)你的意思是if [ -z "${somevar+x}" ]?请问报价还在里面需要[[]]
汤姆·黑尔

@TomHale-是的,在极少数情况下。该[ test程序接受命令行参数,所以常见的展开和解释为命令以通常的方式应该被依赖于测试的调用来渲染应用了,你应该引起被任何程序的命令行,从而读取。测试{!+“!”}
mikeserv

@mikeserv我想你对我的第二个问题是……第一个也是吗?
汤姆·黑尔

1
+1; 当set -u有效且Bash版本低于4.2 时,这似乎是执行此操作的最简单方法。
凯尔·斯特兰德

4

POSIXly:

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

或者,您可以让您的shell为您显示消息:

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

@mikeserv:是的,当然,有很多方法可以做到。首先,我想我将与重复。但是在这个问题中,OP只想检查,他没有声称他想退出或报告变量是否未设置,因此我在subshel​​l中附带了一个检查。
cuonglm

2
好吧,我知道,但这恰恰是因为您必须在类似这样的子shell中进行操作,这表明它可能不是在此处进行测试的最佳方法-这是对失败的停止操作,它不允许任何简单的方法来处理失败。甚至即使一个trap也只能在EXIT上工作。这就是我要说的-只是不能很好地通过/失败。而且这也不是我在说-我之前确实做到了,并且像这样说了一点点评论聊天,以说服我。所以,我只是想我会付钱。
mikeserv

1
@mikeserv:很好,很好。我只想知道,这样做是否会使OP与语法混淆:)
cuonglm

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

我可以想象这效率不高,但是它非常简单且sh兼容,这正是我所需要的。
hoijui

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

...不打印时什么也不会打印。要么...

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

...会告诉您任何一种方式。

您可以使用测试的返回值来动态扩展为适合您的条件的格式字符串:

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

您也可以printf基于替代而失败...

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

...将打印$var does not exist!到stderr并在$var未设置时返回非0 ,但打印$var does exist!到stdout并在$var设置时返回0 。


1

这条简单的代码行得通(并且适用于大多数POSIX shell):

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

或者,以更长的形式编写:

unset var

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

扩展为:

  • 如果var具有值(甚至为null),则将false替换为
  • 如果var具有“无值”,则替换“无值”(空)。

${var+"false"}扩展扩展为“假”“空”。
然后,执行“ nothing”或“ false”,并设置退出代码。

由于退出值是由扩展本身(的执行)设置的,因此无需调用命令test[[[)。


是的,除了某些旧版本的zsh在sh仿真中$IFS包含f,a,l,s或e时。像其他答案一样,可能需要提到数组,哈希或其他类型的变量。
斯特凡Chazelas

我写的@StéphaneChazelas most POSIX shellsmost 意味着In the greatest number of instances,不是全部。……所以,是的,在晦涩的情况下when $IFS contains f, a, l, s or e,对于某些晦涩的外壳,some old versions of zsh此操作失败:真是震撼!我应该假设这样的错误早已解决。……您是否建议我们必须为很久以前的破壳编写代码?

@StéphaneChazelas另外:问题标题非常具体:Bash。

二进制斑马???
mikeserv '18 -10-16

0

纯壳方式:

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

测试脚本:

#!/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"

结果:

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
当变量设置为空字符串时失败。
汤姆·黑尔

0

使用bash 4.4.19,以下对我有用。这是一个完整的例子

$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

您不能使用if命令来检查bash中已声明的变量的存在,但是-v较新的bash中存在该选项,但是该选项不可移植,并且不能在较早的bash版本中使用它。因为当您使用变量(如果不存在)时,它将同时出生。

例如,想象一下我没有使用MYTEST变量或没有给变量赋值,但是当您使用echo命令时,它什么也没显示!或者,如果您使用if [ -z $MYTEST ]它,则返回零值!它没有返回另一个退出状态,告诉您该变量不存在!

现在,您有两种解决方案(不带-v选项):

  1. 使用declare命令。
  2. 使用set命令。

例如:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

但是不幸的是,这些命令显示了您也已在内存中加载了函数!您可以使用declare -p | grep -q MYTEST ; echo $?命令获得更清晰的结果。


-1

检查变量是否已声明/未设置的函数

包括空 $array=()


除了@Gilles的答案

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

-我没有找到将其封装在函数中的方法-我想添加一个简单的版本,该版本部分基于Richard Hansen回答,但也可以解决因空而产生的陷阱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的数据很少,因为只有在未设置变量或空数组的情况下才调用声明。


使用以下代码可以测试功能:

( # 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"
)

脚本应该返回

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

适用于标量数组类型的bash函数:

定义

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
}

调用

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.