bash中声明的和null(空)的定义不一致? 介绍

bash中声明的和null(空)的定义不一致? 介绍,bash,Bash,我试图测试是否声明了一个变量,但没有赋值 根据(引用POSIX规范),声明意味着指定变量的类型,而设置意味着为其赋值。null(空字符串)被视为有效值 实验 然而,仅声明变量也会为其分配一个空字符串: declare var_a [ -n "${var_a+set}" ] && echo "var_a is set to \"$var_a\"" 将其复制/粘贴到终端中,输出为:var\u a设置为“” 在不设置数组的情况下声明数组根据上述定义正常工作: declare -a v

我试图测试是否声明了一个变量,但没有赋值

根据(引用POSIX规范),声明意味着指定变量的类型,而设置意味着为其赋值。null(空字符串)被视为有效值

实验 然而,仅声明变量也会为其分配一个空字符串:

declare var_a
[ -n "${var_a+set}" ] && echo "var_a is set to \"$var_a\""
将其复制/粘贴到终端中,输出为:
var\u a设置为“”

在不设置数组的情况下声明数组根据上述定义正常工作:

declare -a var_b

[ -n "${var_b+set}" ] && echo "var_b is set to \"$var_b\""
这不会给出任何输出

即使使用此语法声明数组,也不会设置它:

var_c=()
[ -n "${var_c+set}" ] && echo "var_c is set to \"$var_c\""
但是设置一个变量,然后再次取消设置它似乎实际上是“取消声明”:

这里最后一条语句的输出是:
var\u d未声明

最后,基于这些实验,我认为声明一个局部变量(在函数中),然后取消设置它,然后再次赋值,实际上会使它成为一个全局变量(因为它是“未声明的”):

仅验证
declare
local
在工作方式上是否相似
(这意味着声明“该选项可以是
declare
接受的任何选项”):

这里最后一条语句的输出是:
var\u f未声明

问题 我想知道我的理解是否不完整,或者bash在这方面不一致

  • 数组和标量变量的这种不同行为背后的基本原理是什么(即标量初始化为一个值,而数组不是)
  • 为什么unset实际上是“未声明的”
  • “声明的”和“集合”的规范定义是什么
  • 如何可靠地确定是否声明了任何变量(数组或标量),但没有设置

  • PS:上面的代码示例可以简单地复制/粘贴到终端会话中(如果您愿意,可以全部在同一会话中)

    我不回答您的所有问题。但是,仅声明标量变量不会为其赋值:

    这将包括3种情况:未设置、设置但为空、设置值

    if [[ -n ${foo+unset} ]]; then
        if [[ -n $foo ]]; then
            echo foo is not empty
        else
            echo foo is empty
        fi
    else
        echo foo is unset
    fi
    
    简单地声明一个变量不会给它一个值:

    $ unset foo
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is unset
    
    $ declare foo
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is unset
    
    $ foo=
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is empty
    
    $ foo=x
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is not empty
    

    我看不到“var_a设置为”“”——您确定这样做时有一个“干净”的外壳吗?当我在macOS的终端应用程序中打开一个新选项卡并粘贴第一个示例的行时,
    var\u a被设置为“
    ”,作为输出;这是一个干净的shell,还是我应该采取其他步骤使其干净?实际上,当我打开一个新选项卡并仅粘贴
    [-n“${var\u a+set}]”和&echo“var\u a”设置为\“$var\u a\”
    时,我不会得到输出。所以我想当我运行
    bash--version
    时,我的shell是干净的,顺便说一句,我得到了
    GNUBash,版本3.2.57(1)-发行版(x86_64-apple-darwin16)
    。也许这就不同了?是的,事实上是这样的:当我用
    GNUBash,版本4.4.12(1)-发行版
    尝试这一点时,它按预期工作。结果是
    GNUBash 4.4.12
    按预期工作,而
    GNUBash 3.2.57
    (默认安装在macOS Sierra中)则没有。感谢你指出这个定义实际上是正确的。格伦·杰克曼,你的问题含蓄地回答了问题1-3;您是否也知道如何可靠地确定变量在任何bash版本中是否未设置?这也可以回答问题4。有bash版本吗?这是POSIX参数扩展语法,不同的版本有不同的行为。我们正在研究bug修复,您不希望必须按照自己的方式编写历史bug代码。你试图解决的问题真的对未设置与设置那么敏感,但却是空的吗?我没有意识到这一点,也明白你的意思;这是一个用于调试/故障排除的布尔函数,我要求提供任何bash版本(我实际上是指兼容POSIX的版本),因为我不想在不同的机器上使用不同的shell运行它时,对意外的结果感到惊讶;自从我上次发表评论以来,我意识到我可以简单地在脚本的开头添加一个前提条件检查,如果它是未经测试的版本,则该检查将失败,要求首先测试是否成功,如果成功,则包括该版本。问题4实际上与是否设置了变量无关<代码>声明
    不创建变量;它设置名称的属性
    declare-a foo
    仅设置名称
    foo
    上的数组属性<代码>声明栏
    实际上没有任何作用。异常出现在函数内部,其中
    declare
    隐式地使名称成为本地名称,这与使用
    local
    命令相同。
    function anotherFunction() {
        local var_f
        [[ $(declare | grep ^var_f) ]] && echo "var_f is declared" || echo "var_f is not declared" # --> declared
    
        var_f=some_value
        [[ $(declare | grep ^var_f) ]] && echo "var_f is declared" || echo "var_f is not declared" # --> declared
    
        unset var_f
        [[ $(declare | grep ^var_f) ]] && echo "var_f is declared" || echo "var_f is not declared" # --> not declared
    }
    
    # local can only be used in a function, hence the function call
    anotherFunction
    
    if [[ -n ${foo+unset} ]]; then
        if [[ -n $foo ]]; then
            echo foo is not empty
        else
            echo foo is empty
        fi
    else
        echo foo is unset
    fi
    
    $ unset foo
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is unset
    
    $ declare foo
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is unset
    
    $ foo=
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is empty
    
    $ foo=x
    $ if [[ -n ${foo+unset} ]]; then if [[ -n $foo ]]; then echo foo is not empty; else echo foo is empty; fi; else echo foo is unset; fi
    foo is not empty