我如何在Bash中知道一个变量是否已经设置?
例如,我如何检查用户是否提供了函数的第一个参数?
function a {
# if $1 is set ?
}
我如何在Bash中知道一个变量是否已经设置?
例如,我如何检查用户是否提供了函数的第一个参数?
function a {
# if $1 is set ?
}
使用test -n "${var-}"
检查变量是否不为空(因此必须已定义/设置)。用法:
if test -n "${var-}"; then
echo "var is set to <$var>"
else
echo "var is not set or empty"
fi
使用test -n "${var+x}"
来检查变量是否已定义/设置(即使为空)。用法:
if test -n "${var+x}"; then
echo "var is set to <$var>"
else
echo "var is not set"
fi
请注意,第一个用例在shell脚本中更为常见,这也是您通常要使用的。
为了理解此解决方案的工作原理,您需要了解POSIX test
命令和POSIX shell参数扩展(规范)的基本知识,因此让我们介绍一下需要了解答案所需的绝对基础知识。
test 命令评估表达式并返回 true 或 false (通过其退出状态)。运算符-n
如果操作数是非空字符串,则返回true。例如,test -n "a"
返回true,而test -n ""
返回false。现在,要检查变量是否为空(这意味着它必须已定义),您可以使用test -n "$var"
。但是,一些shell脚本设置了一个选项(set -u
),该选项会导致对未定义变量的任何引用都会发出错误,因此如果变量var
未定义,则表达式$var
将导致错误。为了正确处理此情况,您必须使用变量扩展,告诉shell如果未定义变量,则将其替换为替代字符串,避免上述错误。
变量扩展${var-}
的含义是:如果变量var
未定义(也称为“unset”),则将其替换为空字符串。因此,test -n "${var-}"
将返回true,如果$var
不为空的话,这几乎总是您想要在shell脚本中检查的内容。反过来检查,即$var
未定义或非空的情况,是test -z "${var-}"
。
现在到第二个用例:检查变量var
是否被定义,无论是否为空。这是一个不那么常见但稍微复杂一些的用例,我建议您阅读Lionel非常好的答案以更好地理解它。
test -z
的解释中不小心加入了一个 "not"。从 test 的 man 页面可以看到:-z STRING
如果 STRING 的长度为零,则返回 true。因此,如果 var 未定义或为空,则 test -z "${var-}"
将返回 true。 - user3750888这个方法适用于我。我希望我的脚本能够在参数未设置时退出并输出错误信息。
#!/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
如果您只想检查值是否设置为VALID或未设置/为空为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"
这是问题的答案。如果你只想检查设置/空值=有效或未设置=无效,请使用此选项。
注意,“..-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(评论),他挑战我准确回答了这个问题。
参考资料:2.6.2 参数扩展
${var:-}
,而不是${var-}
。但在Bash中,我可以确认即使没有冒号也可以工作。 - Palec${var:-}
和${var-}
有不同的含义。${var:-}
在变量var
未定义或为空时扩展为空字符串,而${var-}
仅在未定义时扩展为空字符串。 - RubenLaguna请阅读 bash
手册中的“参数扩展”部分。参数扩展并不提供一般性的检测变量是否已设置,但如果未设置参数,有几件事情可以做。
例如:
function a {
first_arg=${1-foo}
# rest of the function
}
如果已经分配了,将把first_arg
设置为$1
,否则它使用值"foo"。如果必须保证a
仅接受一个参数,并且不存在好的默认值,则可以在未提供参数时退出并显示错误消息:
function a {
: ${1?a must take a single argument}
# rest of the function
}
(请注意在这个例子中使用:
作为一个空命令,它只是展开其参数的值。我们不想在这个例子中对$1
做任何操作,如果它没有设置就退出。)
: ${var?message}
。 - tripleee为了清晰回答楼主的问题,即如何确定变量是否设置,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 |
+----------------------+-------------+---------+------------+
要检查变量是否设置了非空值,可以使用[ -n "$x" ]
,正如其他人已经指出的那样。
大多数情况下,将具有空值的变量与未设置的变量视为相同的变量是一个好主意。但是如果需要区分这两种情况,则可以这样做:[ -n "${x+set}" ]
("${x+set}"
会扩展为set
,如果x
已设置,则为空字符串)。
要检查参数是否已传递,请测试$#
,它是传递给函数(或脚本,不在函数中时)的参数数目(请参阅Paul的答案)。
我很惊讶没有人尝试编写一个shell脚本来程序化生成那个众所周知难以理解的表格。既然我们在这里尝试学习编码技巧,为什么不用代码表达答案呢? :) 这是我的方法(应该适用于任何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
# "${N:?x}" "${U:?x} "
printf "$R" "\${FOO:?x}" "${S:?x}" "<error>" "<error> "; S='V';N='';unset U
# "${U?x} "
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" "----------" "-------" "-------" "---------";
运行脚本后的输出结果:
+------------+------------+------------+------------+
| 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 | |
+------------+------------+------------+------------+
这个脚本缺少一些功能,比如无法显示副作用赋值的时间(是否发生),但也许有更有雄心壮志的人想要以此为起点并加以改进。
[[ ]]
内置命令中使用-v
:#! /bin/bash -u
if [[ ! -v SOMEVAR ]]; then
SOMEVAR='hello'
fi
echo $SOMEVAR
if [[ -v mytest ]]; then echo "this runs only if variable is set"; fi
if [[ ! -v mytest2 ]]; then echo "this runs only if variable is not set"; fi
测试变量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
,它可以保护我们免受常见错误的影响,例如变量名称中的错别字。我建议使用它,但这意味着必须正确处理未设置变量和设置为空字符串的变量之间的区别。