ChatGPT解决这个技术问题 Extra ChatGPT

在 Bash 中测试非零长度字符串: [ -n "$var" ] 或 [ "$var" ]

我见过 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

或者

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi

D
Dennis Williamson

编辑:这是一个更完整的版本,显示了 [(又名 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 的双括号(第 1b - 4b 列)

请注意,在第 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

谢谢!我决定采用“在单括号中引用变量(第 2a 列)”IMO 的风格,-n 只会增加噪音并降低可读性。同样,对于零长度或未设置的测试,我将使用 [ ! "$var" ] 而不是 [ -z "$var" ]。
因此,您的 ["[-n"(OP 的第一个问题)的图表显示它们是完全等价的,对吧?
@hobs:是的,使用哪个取决于哪个更清楚。当使用双括号形式时,您还会注意到它们的等价物,这是使用 Bash 时首选的形式。
所以,总结一下你很棒的表,测试非空字符串的更清晰(“更好”)的方法是 [["
c
codeforester

就 Bash 而言,最好使用 more powerful [[

通常情况

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

上述两个结构看起来干净且可读。在大多数情况下,它们应该足够了。

请注意,我们不需要在 [[ 中引用变量扩展,因为 word splittingglobbing 没有危险。

为了防止 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 [[


我认为使用 [[ 不一定更好。这是个人喜好的问题。即使您引用的 the link 也建议始终使用 []
Z
Zombo

这里还有一些测试

如果字符串不为空,则为真:

[ -n "$var" ]
[[ -n $var ]]
test -n "$var"
[ "$var" ]
[[ $var ]]
(( ${#var} ))
let ${#var}
test "$var"

如果字符串为空,则为真:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"

这并不能回答 OP 关于什么是更好的方法的问题。
p
papacharlie

正确答案如下:

if [[ -n $var ]] ; then
  blah
fi

请注意 [[...]] 的使用,它为您正确处理引用变量。


为什么在 Bash 中不需要它时使用 -n
@codeforester BASH(Bourne Again Shell)是 Bourne Shell 的后代,因此它继承了 -n-z 测试运算符,但随后在其上添加了 ! 否定。此外,这在概念上与高级语言中 ==!= 之间的区别相同,高级语言提供肯定和否定运算符只是为了扩展可能的语义范围。有时组装一个不需要依赖双重否定等的表达式要容易得多。
P
Peter Mortensen

评估空环境变量的另一种可能更透明的方法是使用...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi

这是伯恩贝壳时代的老派。不要延续这个旧习惯。 bash 是比其前身更锋利的工具。
如果您要避免 POSIX 标准明确标记为过时的语法,您甚至不需要使用 pre-bash shell。 [ "$ENV_VARIABLE" != "" ] 将在具有符合 POSIX 标准的 test 实现的每个 shell 上工作——不仅仅是 bash,还有 ash/dash/ksh/etc。
...也就是说,不要使用 -a-o 来组合测试,而是使用 [ ... ] && [ ... ][ ... ] || [ ... ] 并且唯一可以应用于在测试中使用任意变量数据的极端情况是明确关闭。
P
Peter Mortensen

使用 case/esac 测试:

case "$var" in
  "") echo "zero length";;
esac

不谢谢。这不是 case 的用途。
当有两个以上的选择时,case 效果最好。
case 不适用于此类用途。