Posix shell:区分空变量和不存在变量
Posix shell: distinguish between empty and not existing variable
在纯 /bin/sh
中,我如何区分空变量、未设置变量和不存在(未定义)变量。
案例如下:
# Case 1: not existing
echo "${foo}"
# Case 2: unset
foo=
echo "${foo}"
# Case 3: Empty
foo=""
echo "${foo}"
现在我想检查这三个案例中的每一个。
如果case 2和case 3其实是一样的,那我至少要能和case 1区分开来
有什么想法吗?
更新
感谢 Matteo
解决了
代码如下所示:
#foo <-- not defined
bar1=
bar2=""
bar3="a"
if ! set | grep '^foo=' >/dev/null 2>&1; then
echo "foo does not exist"
elif [ -z "${foo}" ]; then
echo "foo is empty"
else
echo "foo has a value"
fi
if ! set | grep '^bar1=' >/dev/null 2>&1; then
echo "bar1 does not exist"
elif [ -z "${bar1}" ]; then
echo "bar1 is empty"
else
echo "bar1 has a value"
fi
if ! set | grep '^bar2=' >/dev/null 2>&1; then
echo "bar2 does not exist"
elif [ -z "${bar2}" ]; then
echo "bar2 is empty"
else
echo "bar2 has a value"
fi
if ! set | grep '^bar3=' >/dev/null 2>&1; then
echo "bar3 does not exist"
elif [ -z "${bar3}" ]; then
echo "bar3 is empty"
else
echo "bar3 has a value"
fi
结果:
foo does not exist
bar1 is empty
bar2 is empty
bar3 has a value
您可以使用set
If no options or arguments are specified, set shall write the names and values of all shell variables in the collation sequence of the current locale. Each name shall start on a separate line, using the format:
您可以列出所有变量 (set
) 并 grep 查找您要检查的变量名称
set | grep '^foo='
我不知道 sh
,但在 bash
和 dash
中,您可以对案例 1 和案例 2/3 执行 echo ${TEST:?Error}
。快速浏览 wikibooks,它似乎也适用于 bourne shell。
你可以在 bash 和破折号中像这样使用它(使用 $? 获取错误代码)
echo ${TEST:?"Error"}
bash: TEST: Error
[lf@dell:~/tmp/soTest] echo $?
1
[lf@dell:~/tmp/soTest] TEST2="ok"
[lf@dell:~/tmp/soTest] echo ${TEST2:?"Error"}
ok
[lf@dell:~/tmp/soTest] echo $?
0
[lf@dell:~/tmp/soTest] dash
$ echo ${TEST3:?"Error"}
dash: 1: TEST3: Error
$ TEST3=ok
$ echo ${TEST3:?"Error"}
ok
如果 var 未设置,您可以使用 ${var?} 语法抛出错误,如果 var 未设置或为空,则可以使用 ${var:?} 语法抛出错误。举个具体的例子:
$ unset foo
$ test -z "${foo?unset}" && echo foo is empty || echo foo is set to $foo
-bash: foo: unset
$ foo=
$ test -z "${foo?unset}" && echo foo is empty || echo foo is set to $foo
foo is empty
$ foo=bar
$ test -z "${foo?unset}" && echo foo is empty || echo foo is set to $foo
foo is set to bar
我在这里还没有看到 POSIX 合规 的正确答案。首先让我重申 William Pursell 的断言,即代码 foo=
确实将变量 foo
设置为与 foo=""
相同的空值。要取消设置 foo
,必须永远不要设置或取消设置 unset foo
。
Matteo的回答是正确的,但有一些注意事项。当您 运行 set
in bash 和 posix
模式被禁用时,它也会打印所有定义的函数。可以这样抑制:
isvar() (
[ -n "$BASH" ] && set -o posix
set | grep -q "^="
)
把它写成sub-shell函数,我们就不用担心bash的posix
设置完成后的状态了。
但是,你仍然可以从值包含回车returns的变量中得到false-positives,所以理解这不是100%万无一失的。
$ a="
> b=2
> "
$ set | grep ^b=
b=2
因此,为了获得最大的正确性,您可以利用 bash 的 -v
测试(如果可用)。
isvar() {
if [ -n "$BASH" ]; then
[ -v "" ]
else
set | grep -q "^="
fi
}
也许有人在某个地方有一个库也支持其他 shell 的扩展。从本质上讲,这是 POSIX 规范中的一个弱点,它只是没有被视为保证修正。
在纯 /bin/sh
中,我如何区分空变量、未设置变量和不存在(未定义)变量。
案例如下:
# Case 1: not existing
echo "${foo}"
# Case 2: unset
foo=
echo "${foo}"
# Case 3: Empty
foo=""
echo "${foo}"
现在我想检查这三个案例中的每一个。 如果case 2和case 3其实是一样的,那我至少要能和case 1区分开来
有什么想法吗?
更新 感谢 Matteo
解决了代码如下所示:
#foo <-- not defined
bar1=
bar2=""
bar3="a"
if ! set | grep '^foo=' >/dev/null 2>&1; then
echo "foo does not exist"
elif [ -z "${foo}" ]; then
echo "foo is empty"
else
echo "foo has a value"
fi
if ! set | grep '^bar1=' >/dev/null 2>&1; then
echo "bar1 does not exist"
elif [ -z "${bar1}" ]; then
echo "bar1 is empty"
else
echo "bar1 has a value"
fi
if ! set | grep '^bar2=' >/dev/null 2>&1; then
echo "bar2 does not exist"
elif [ -z "${bar2}" ]; then
echo "bar2 is empty"
else
echo "bar2 has a value"
fi
if ! set | grep '^bar3=' >/dev/null 2>&1; then
echo "bar3 does not exist"
elif [ -z "${bar3}" ]; then
echo "bar3 is empty"
else
echo "bar3 has a value"
fi
结果:
foo does not exist
bar1 is empty
bar2 is empty
bar3 has a value
您可以使用set
If no options or arguments are specified, set shall write the names and values of all shell variables in the collation sequence of the current locale. Each name shall start on a separate line, using the format:
您可以列出所有变量 (set
) 并 grep 查找您要检查的变量名称
set | grep '^foo='
我不知道 sh
,但在 bash
和 dash
中,您可以对案例 1 和案例 2/3 执行 echo ${TEST:?Error}
。快速浏览 wikibooks,它似乎也适用于 bourne shell。
你可以在 bash 和破折号中像这样使用它(使用 $? 获取错误代码)
echo ${TEST:?"Error"}
bash: TEST: Error
[lf@dell:~/tmp/soTest] echo $?
1
[lf@dell:~/tmp/soTest] TEST2="ok"
[lf@dell:~/tmp/soTest] echo ${TEST2:?"Error"}
ok
[lf@dell:~/tmp/soTest] echo $?
0
[lf@dell:~/tmp/soTest] dash
$ echo ${TEST3:?"Error"}
dash: 1: TEST3: Error
$ TEST3=ok
$ echo ${TEST3:?"Error"}
ok
如果 var 未设置,您可以使用 ${var?} 语法抛出错误,如果 var 未设置或为空,则可以使用 ${var:?} 语法抛出错误。举个具体的例子:
$ unset foo
$ test -z "${foo?unset}" && echo foo is empty || echo foo is set to $foo
-bash: foo: unset
$ foo=
$ test -z "${foo?unset}" && echo foo is empty || echo foo is set to $foo
foo is empty
$ foo=bar
$ test -z "${foo?unset}" && echo foo is empty || echo foo is set to $foo
foo is set to bar
我在这里还没有看到 POSIX 合规 的正确答案。首先让我重申 William Pursell 的断言,即代码 foo=
确实将变量 foo
设置为与 foo=""
相同的空值。要取消设置 foo
,必须永远不要设置或取消设置 unset foo
。
Matteo的回答是正确的,但有一些注意事项。当您 运行 set
in bash 和 posix
模式被禁用时,它也会打印所有定义的函数。可以这样抑制:
isvar() (
[ -n "$BASH" ] && set -o posix
set | grep -q "^="
)
把它写成sub-shell函数,我们就不用担心bash的posix
设置完成后的状态了。
但是,你仍然可以从值包含回车returns的变量中得到false-positives,所以理解这不是100%万无一失的。
$ a="
> b=2
> "
$ set | grep ^b=
b=2
因此,为了获得最大的正确性,您可以利用 bash 的 -v
测试(如果可用)。
isvar() {
if [ -n "$BASH" ]; then
[ -v "" ]
else
set | grep -q "^="
fi
}
也许有人在某个地方有一个库也支持其他 shell 的扩展。从本质上讲,这是 POSIX 规范中的一个弱点,它只是没有被视为保证修正。