本文共 17606 字,大约阅读时间需要 58 分钟。
我如何知道是否在中设置了变量?
例如,如何检查用户是否将第一个参数赋予函数?
function a { # if $1 is set ?}
检查是否设置了变量
var=""; [[ $var ]] && echo "set" || echo "not 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 "" ]
相同的值(true))。
但是,尽管可以安全地省略引号,并且所有人都不会立即看到他也是主要的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 [[ $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 allif [[ ! ${1+x} ]]; then ... # We were called with no arguments
如果您想测试一个变量是否绑定或未绑定,即使您已启用名词集选项,该方法也能很好地工作:
set -o noun setif printenv variableName >/dev/null; then # variable is bound to a valueelse # variable is unboundfi
这是测试参数是否为unset ,为空(“ Null”)或设置为值的方法 :
+--------------------+----------------------+-----------------+-----------------+| | 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 |+--------------------+----------------------+-----------------+-----------------+
来源: :
在所有显示为“替换”的情况下,表达式都将替换为显示的值。 在所有显示为“ assign”的情况下,都会为参数分配该值,该值也会替换表达式。
if [[ ${1:+isset} ]]then echo "It was set and not null." >&2else echo "It was not set or it was null." >&2fiif [[ ${1+isset} ]]then echo "It was set but might be null." >&2else echo "It was was not set." >&2fi
尽管此处介绍的大多数技术都是正确的,但bash 4.2支持对变量( )的存在进行实际测试,而不是测试变量的值。
[[ -v foo ]]; echo $?# 1foo=bar[[ -v foo ]]; echo $?# 0foo=""[[ -v foo ]]; echo $?# 0
值得注意的是,与许多其他方法(例如使用[ -z
不同,此方法在set -u
/ set -o nounset
模式下用于检查未设置的变量时不会导致错误。
在现代版本的Bash(我认为是4.2或更高版本;我不确定)上,我会尝试这样做:
if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigilthen 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
我总是使用这一代码,是基于这样的事实,即任何首次看到该代码的人都容易理解:
if [ "$variable" = "" ] then echo "Variable X is empty"fi
并且,如果要检查是否不为空;
if [ ! "$variable" = "" ] then echo "Variable X is not empty"fi
而已。
启用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.}
相关:
如果您想检查$@
任何内容,我发现了一个(更好)的代码可以执行此操作。
if [[ $1 = "" ]]then echo '$1 is blank'else echo '$1 is filled up'fi
为什么这一切呢? $@
所有内容都存在于Bash中,但默认情况下为空白,因此test -z
和test -n
无法帮助您。
更新:您还可以计算参数中的字符数。
if [ ${#1} = 0 ]then echo '$1 is blank'else echo '$1 is filled up'fi
[[ $foo ]]
要么
(( ${#foo} ))
要么
let ${#foo}
要么
declare -p foo
在中,可以使用-z
运算符,如果字符串的长度为零,则该运算符为True。
一个简单的单行代码,用于设置默认的MY_VAR
如果未设置),否则可以显示以下消息:
[[ -z "$MY_VAR" ]] && MY_VAR="default"[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
使用[[ -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
输出,而不会影响其返回代码。
我更喜欢的方式是这样的:
$var=10$if ! ${var+false};then echo "is set";else echo "NOT set";fiis set$unset var$if ! ${var+false};then echo "is set";else echo "NOT set";fiNOT set
因此,基本上,如果设置了变量,它将变为“对结果false
的否定”(将为true
为“已设置”)。
并且,如果未设置,它将成为“对结果true
的否定”(因为空结果的值为true
)(因此将以false
=“ NOT set”结束)。
这是我每天使用的:
## Check if a variable is set# param1 name of the variable#function is_set(){ [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"}
在Linux和Solaris(低至bash 3.0)下,此方法效果很好。
bash-3.00$ myvar="TEST"bash-3.00$ is_set myvar ; echo $?0bash-3.00$ mavar=""bash-3.00$ is_set myvar ; echo $?0bash-3.00$ unset myvarbash-3.00$ is_set myvar ; echo $?1
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi
在bash中,您可以在内置[[ ]]
使用-v
:
#! /bin/bash -uif [[ ! -v SOMEVAR ]]; then SOMEVAR='hello'fiecho $SOMEVAR
由于bash
标签,我给出了以Bash为重点的答案。
只要您仅在Bash中处理命名变量,此函数就应该始终告诉您变量是否已设置,即使它是一个空数组。
is-variable-set() { declare -p $1 &>dev/null}
在Bash中(至少可以追溯到3.0),如果var
是一个声明/设置变量,则declare -p var
输出一条declare
命令,该命令会将变量var
设置为其当前类型和值,并返回状态码0
(成功)。 如果var
是未申报,然后declare -p var
输出错误消息以stderr
,并返回状态码1
。 使用&>/dev/null
,将常规的stdout
和stderr
输出都重定向到/dev/null
,永远不会被看到,并且不会更改状态码。 因此,该函数仅返回状态码。
[ -n "$var" ]
:这仅检查${var[0]}
是否为空。 (在Bash中,$var
与${var[0]}
。)[ -n "${var+x}" ]
:仅检查${var[0]}
是否设置。[ "${#var[@]}" != 0 ]
:这仅检查是否设置了$var
至少一个索引。
这仅适用于命名变量(包括$_
),不适用于某些特殊变量( $!
, $@
, $#
, $$
, $*
, $?
, $-
, $0
, $1
, $2
,...和任何其他变量我可能已经忘记了)。 由于这些都不是数组,因此POSIX样式[ -n "${var+x}" ]
适用于所有这些特殊变量。 但是要当心将其包装在函数中,因为在调用函数时许多特殊变量会更改值/存在。
如果您的脚本具有数组,并且您正尝试使其与尽可能多的Shell兼容,则可以考虑使用typeset -p
而不是declare -p
。 我读过ksh仅支持前者,但无法对其进行测试。 我确实知道Bash 3.0+和Zsh 5.5.1都支持typeset -p
和declare -p
,区别仅在于其中一个是另一种。 但是我没有测试过这两个关键字以外的差异,也没有测试过其他shell。
如果您需要脚本与POSIX sh兼容,则不能使用数组。 如果没有数组,则[ -n "{$var+x}" ]
可以工作。
此功能取消设置变量var
, eval
S中通过代码,运行测试,以确定是否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 0is-var-set-after "var=(foo)" # 0 0 0 0is-var-set-after "var=([0]=foo)" # 0 0 0 0is-var-set-after "var=([x]=foo)" # 0 0 0 0is-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 0is-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 0is-var-set-after "var=([1]=foo)" # 1 1 0 0is-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 0is-var-set-after "declare -a var" # 1 1 1 0is-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 0declare -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
(100%?)对于测试Bash中的命名变量至少是3.0可靠。[ -n "${var+x}" ]
在符合POSIX的情况下是可靠的,但不能处理数组。- 还存在其他测试,用于检查变量是否为非空,以及检查其他外壳程序中已声明的变量。 但是这些测试都不适合Bash和POSIX脚本。
我喜欢辅助功能来隐藏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 declaredfunction 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 functionsfunction die(){ echo "Error: $1"; exit 1}function assertVariableDeclared(){ IsDeclared "$1" || die "variable not declared: $1"}function expect(){ while (( $# > 0 )); do assertVariableDeclared $1; shift done}# actual examplefunction exampleFunction(){ expectVariables inputStr outputStr outputStr="$inputStr world!"}function bonus(){ local inputStr='Hello' local outputStr= # remove this to trigger error exampleFunction echo $outputStr}bonus
如果全部调用都需要声明变量:
你好,世界!
其他:
错误:未声明变量:outputStr
$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=()
,则进行声明的调用将确保我们得到正确的结果 此功能将在以下情况下进行测试:
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
。
更多细节
和测试脚本看到我的这个问题 。
备注: 的也显示了declare -p
的类似用法,这确实是巧合。 否则我当然会相信它!
if [ "$1" != "" ]; then echo \$1 is setelse echo \$1 is not setfi
虽然对于参数,通常我认为最好测试$#,这是参数的数量。
if [ $# -gt 0 ]; then echo \$1 is setelse echo \$1 is not setfi
执行此操作的方法有很多种,其中之一是:
if [ -z "$1" ]
如果$ 1为null或未设置,则此操作成功
你可以做:
function a { if [ ! -z "$1" ]; then echo '$1 is set' fi}
要检查非空/非零字符串变量,即是否已设置,请使用
if [ -n "$1" ]
与-z
相反。 我发现自己使用-n
而不是-z
。
您将使用它像:
if [ -n "$1" ]; then echo "You supplied the first parameter!"else echo "First parameter not supplied."fi
要检查变量是否设置为非空值,请使用[ -n "$x" ]
,正如其他变量已经指出的那样。
在大多数情况下,以与未设置的变量相同的方式对待具有空值的变量是一个好主意。 但是你可以区分这两种,如果你需要: [ -n "${x+set}" ]
"${x+set}"
扩展为set
,如果x
被设置,为空字符串,如果x
是未设置)。
要检查是否已传递参数,请测试$#
,它是传递给函数(或不在函数中时传递给脚本)的参数数量(请参阅 )。
对于那些在的脚本中查找unset 或为空的对象 :
if [ -z "${var-}" ]; then echo "Must provide var environment variable. Exiting...." exit 1fi
常规[ -z "$var" ]
检查将因var; unbound variable
而失败var; unbound variable
如果set -u
但未var; unbound variable
,但如果var; unbound variable
set -u
var
而没有失败,则[ -z "${var-}" ]
为空字符串。
这对我有用。 如果未设置参数,我希望脚本退出并显示错误消息。
#!/usr/bin/env bashset -o errexit# Get the value and empty validation check all in oneVER="${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
如果您只想检查值set = VALID或unset / empty = INVALID,请尝试此选项。
TSET="good val"TEMPTY=""unset TUNSETif [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi# VALIDif [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi# INVALIDif [ "${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 TUNSETif [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi# VALIDif [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi# VALIDif [ "${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(评论),他要求我准确回答这个问题。
参考
我总是在发现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 [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fiecho "SOME_VAR=$SOME_VAR"
如果您不SOME_VAR
而不是我的$SOME_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
进行任何操作,如果未设置,则退出)
转载地址:http://wddnb.baihongyu.com/