我见过Bash脚本用两种不同的方式测试长度为非零的字符串。大多数脚本使用-n选项:
#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
# Do something when var is non-zero length
fi
但是-n选项其实并不需要:
# Without the -n option
if [ "$var" ]; then
# Do something when var is non-zero length
fi
哪一种方法更好?
类似地,哪种是测试零长度的更好方法:
if [ -z "$var" ]; then
# Do something when var is zero-length
fi
or
if [ ! "$var" ]; then
# Do something when var is zero-length
fi
不幸的是,我还没有找到为什么有两种语法放在首位。两者似乎都是在1979年Unix版本7中同时引入的。
1994年的单一Unix规范首选-n和-z语法。从那以后,同样的建议被逐字复制到POSIX的每个版本中,包括最新版本POSIX.1-2017:
这两条命令:
测试" $ 1 "
测试!" $ 1 "
不能在某些历史系统上可靠地使用。意想不到的
如果使用这样的字符串表达式并且$1
扩展为'!', '(',或已知的一元主元。更好的构念是:
Test -n "$1"
Test -z "$1"
分别。
我认为,如果你想学而不实地遵循规范中的每一条建议,那么你应该继续使用-n和-z,但说真的,你需要兼容超过28年的系统吗?
在我看来,if ["$var"]和if [!"$var"]使得该语法成为明显的赢家。但底线是没有功能上的区别,所以随你的个人喜好去吧。
就Bash而言,最好使用更强大的[[。
通常情况下
if [[ $var ]]; then # var is set and it is not empty
if [[ ! $var ]]; then # var is not set or it is set to an empty string
上面两个结构看起来干净且可读。在大多数情况下,它们应该足够了。
请注意,我们不需要在[[]内部引用变量展开,因为没有词分裂和通配符的危险。
防止shellcheck的软投诉[[$var]]和[[!$var]],我们可以使用-n选项。
极少数情况下
在极少数情况下,我们必须区分“被设置为空字符串”和“根本不被设置”,我们可以使用这些:
if [[ ${var+x} ]]; then # var is set but it could be empty
if [[ ! ${var+x} ]]; then # var is not set
if [[ ${var+x} && ! $var ]]; then # var is set and is empty
我们也可以用-v检验:
if [[ -v var ]]; then # var is set but it could be empty
if [[ ! -v var ]]; then # var is not set
if [[ -v var && ! $var ]]; then # var is set and is empty
if [[ -v var && -z $var ]]; then # var is set and is empty
相关职位和文件
有很多与此相关的帖子。以下是一些例子:
如何检查一个变量是否设置在Bash?
如何检查环境变量是否存在并获取其值?
如何在Bash中找到变量是否为空
“加号”(“+:”)在shell脚本表达式中是什么意思?
在Bash中,双方括号[[]]比单方括号[]更可取吗?
Bash中单方括号和双方括号的区别是什么?
mklement0给出了一个很好的回答,他谈到了[[vs []
Bash Hackers Wiki - [vs []
不幸的是,我还没有找到为什么有两种语法放在首位。两者似乎都是在1979年Unix版本7中同时引入的。
1994年的单一Unix规范首选-n和-z语法。从那以后,同样的建议被逐字复制到POSIX的每个版本中,包括最新版本POSIX.1-2017:
这两条命令:
测试" $ 1 "
测试!" $ 1 "
不能在某些历史系统上可靠地使用。意想不到的
如果使用这样的字符串表达式并且$1
扩展为'!', '(',或已知的一元主元。更好的构念是:
Test -n "$1"
Test -z "$1"
分别。
我认为,如果你想学而不实地遵循规范中的每一条建议,那么你应该继续使用-n和-z,但说真的,你需要兼容超过28年的系统吗?
在我看来,if ["$var"]和if [!"$var"]使得该语法成为明显的赢家。但底线是没有功能上的区别,所以随你的个人喜好去吧。
编辑:这是一个更完整的版本,显示了[(又名test)和[[之间的更多差异。
下表显示了变量是否带引号,使用单括号还是双括号,以及变量是否只包含空格,这些都影响使用带-n/-z的测试是否适合检查变量。
| 1a 2a 3a 4a 5a 6a | 1b 2b 3b 4b 5b 6b
| [ [" [-n [-n" [-z [-z" | [[ [[" [[-n [[-n" [[-z [[-z"
-----+------------------------------------+------------------------------------
unset| false false true false true true | false false false false true true
null | false false true false true true | false false false false true true
space| false true true true true false| true true true true false false
zero | true true true true false false| true true true true false false
digit| true true true true false false| true true true true false false
char | true true true true false false| true true true true false false
hyphn| true true true true false false| true true true true false false
two | -err- true -err- true -err- false| true true true true false false
part | -err- true -err- true -err- false| true true true true false false
Tstr | true true -err- true -err- false| true true true true false false
Fsym | false true -err- true -err- false| true true true true false false
T= | true true -err- true -err- false| true true true true false false
F= | false true -err- true -err- false| true true true true false false
T!= | true true -err- true -err- false| true true true true false false
F!= | false true -err- true -err- false| true true true true false false
Teq | true true -err- true -err- false| true true true true false false
Feq | false true -err- true -err- false| true true true true false false
Tne | true true -err- true -err- false| true true true true false false
Fne | false true -err- true -err- false| true true true true false false
如果你想知道一个变量的长度是否为非零,请执行以下任何操作:
在单括号中引用变量(第2a列)
使用-n并在单括号中引用变量(第4a列)
使用双括号,带引号或不带引号,带-n或不带-n(列1b - 4b)
请注意,从标记为“two”的行开始的列1a中,结果表明[正在计算变量的内容,就像它们是条件表达式的一部分一样(结果与描述列中的“T”或“F”所暗示的断言匹配)。当使用[[(列1b)时,变量内容被视为字符串而不计算。
列3a和5a中的错误是由变量值包含空格和变量未加引号引起的。同样,如列3b和5b所示,[[将变量的内容计算为字符串。
相应地,对于零长度字符串的测试,列6a、5b和6b显示了正确的方法。还要注意,如果否定比使用相反的操作更清楚地表明意图,则可以对任何这些测试执行否定。例如:if ![[-n $var]]。
如果使用[,确保不会得到意外结果的关键是引用变量。使用[[,没有关系。
被抑制的错误消息是“期望一元运算符”或“期望二元运算符”。
这是生成上面表格的脚本。
#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal
dw=5 # description column width
w=6 # table column width
t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }
o=/dev/null
echo ' | 1a 2a 3a 4a 5a 6a | 1b 2b 3b 4b 5b 6b'
echo ' | [ [" [-n [-n" [-z [-z" | [[ [[" [[-n [[-n" [[-z [[-z"'
echo '-----+------------------------------------+------------------------------------'
while read -r d t
do
printf '%-*s|' "$dw" "$d"
case $d in
unset) unset t ;;
space) t=' ' ;;
esac
[ $t ] 2>$o && t || f
[ "$t" ] && t || f
[ -n $t ] 2>$o && t || f
[ -n "$t" ] && t || f
[ -z $t ] 2>$o && t || f
[ -z "$t" ] && t || f
echo -n "|"
[[ $t ]] && t || f
[[ "$t" ]] && t || f
[[ -n $t ]] && t || f
[[ -n "$t" ]] && t || f
[[ -z $t ]] && t || f
[[ -z "$t" ]] && t || f
echo
done <<'EOF'
unset
null
space
zero 0
digit 1
char c
hyphn -z
two a b
part a -a
Tstr -n a
Fsym -h .
T= 1 = 1
F= 1 = 2
T!= 1 != 2
F!= 1 != 1
Teq 1 -eq 1
Feq 1 -eq 2
Tne 1 -ne 2
Fne 1 -ne 1
EOF