为什么即使变量为空, test -n 也会返回 true?我究竟做错了什么?

为什么即使变量为空, test -n 也会返回 true?我究竟做错了什么?

根据test(1)手册页:

       -n STRING
              the length of STRING is nonzero

所以我预计这会运行良好:

[ -n ${var} ] && echo "var is not empty"

我在实际案例中使用了这个逻辑,脚本如下:

[...]
dne() {
    echo DEBUG: wc -c: $(echo -n $peopkg |wc -c)
    echo DEBUG: pdir: $pdir
    echo "Error: ${package}: doesn't exist in local repos"
    exit 1
}


# packages are listed as such: 
# p/pname/package-version-release-distrelease...
# pname is inconsistent, but it is guaranteed that the first word in package
# (all to lowercase) will match at least one pname.
# NOTE: package-[0-9]* matches package-32bit. *-32bit is a subpackage we want
# to match later, but not when it's not already in pcase.
# So that must be negated too

pfirst=${pcase%%-*}
for pdir in ${p}/${pfirst}*
do
    # check if the glob matched anything at all
    [ ${pdir} = "${p}/${pfirst}*" ] && dne

    peopkg=$(find ${pdir} \
        -name ${package}-[0-9]* \
        ! -name *.delta.eopkg \
        ! -name ${package}-32bit* |sort -rn |head -1)
    echo DEBUG: in-loop peopkg: $peopkg
    echo DEBUG: in-loop wc -c: $(echo -n $peopkg |wc -c)
    echo DEBUG: in-loop test -n: $(test -n $peopkg && echo true || echo false)
    #------------------------------------------------------------#
    # break on ANY match. There's supposed to be only one anyway #
    [ -n ${peopkg} ] && break # <--- [issue here]                #
    #------------------------------------------------------------#
done
[ -z ${peopkg} ] && dne
[...]

这里重要的是,当我运行它时,我收到以下消息:

DEBUG: in-loop peopkg:
DEBUG: in-loop wc -c: 0
DEBUG: in-loop test -n: true
DEBUG: wc -c: 0
DEBUG: pdir: a/alsa-firmware
Error: alsa-utils: doesn't exist in local repos

这对我来说毫无意义。DEBUG: pdir: a/alsa-firmware表明循环始终在第一次迭代时退出。仅当 glob 模式 a/alsa* 与某些内容匹配并且 peopkg 的长度非零时才会发生这种情况。

PS:我正在努力兼容 POSIX。

答案1

如果var包含空字符串,则[ -n $var ]扩展(分词后$var)为单词[,-n]。这是 的单参数版本test,它测试该单个参数是否非空。该字符串-n不为空,因此测试为真。

在 GNU 手册页中,在您引用的段落之后提到了这一点:

   -n STRING
          the length of STRING is nonzero

   STRING equivalent to -n STRING

问题当然是缺乏引用,例如为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?什么时候需要双引号?

请注意,在不带引号的情况下,不仅空字符串会被破坏。如果var包含多个单词也会出现问题:

$ var='foo bar'; [ -n $var ]
bash: [: foo: binary operator expected

或通配符:

$ var='*'; [ -n $var ]
bash: [: file.txt: binary operator expected

答案2

-n不适用于未加引号的变量。
正如联机帮助页所示,-n测试非零长度字符串
因此,当测试变量是否使用它扩展为空时,我们必须引用该变量。

所以这个问题的解决方法是[ -n "${peopkg}" ]

答案3

是的,似乎没有字符串不为空。但无论如何你都应该引用变量,因为值有空格。

例如

a="hello, world"
b=""

test -n "$a"
test -n "$b"

相关内容