`declare name` 和 `declare -g` 有什么作用?

Tim*_*Tim 4 bash

  1. 有什么作用

    declare name
    
    Run Code Online (Sandbox Code Playgroud)

    不提供任何选项吗?它是否声明了字符串变量的名称?

  2. 有什么作用

    declare -g
    
    Run Code Online (Sandbox Code Playgroud)

    不提供变量名怎么办?它是否显示具有全局属性的所有变量的值?

declare在 Bash 手册的描述中没有找到答案。

谢谢。

Sté*_*las 15

shell 中的变量处理和作用域,尤其是bash可能非常晦涩和不直观(有时还有错误)。

kshtypeset一个类似的功能。ksh, zsh,yashtypeset. bash具有typeset作为一个别名declare用于与兼容kshzsh具有declare作为一个别名typeset为兼容bash。大多数外壳都有export,readonly并且local实现了部分typeset功能。

一个为什么的原因bash作者选择declaretypeset可能是因为typeset没有只设置类型,它也声明一个变量:介绍它在给定的范围内,可能与类型,属性和/或值。

在 中bash,变量可以是:

  • 未知(比如它们从未被设置或声明)
  • 声明(之后declare
  • 设置(当给定值时,可能为空)。

它们可以是不同的类型:

  • 标量
  • 大批
  • 关联数组

并有几个属性:

  • 整数
  • 出口
  • 只读
  • 所有小写​​/大写
  • 命名引用

(尽管类型和属性之间的区别可能非常模糊)。

并非所有类型和属性的组合都受支持或有效。

现在,declare在当前范围内声明变量。bash,即使它实现了动态作用域,它也会特别对待最外层的作用域。它称之为全局作用域。

declare全局范围内和在函数中调用时(我不是在谈论由子外壳引入或与环境相关联的那种单独范围),它的行为非常不同。

当您declare var在函数内部执行并提供未在同一范围内声明相同的变量时,它会声明一个变量,该变量最初未设置,并且隐藏了本var应存在于父范围中的潜在变量(调用方功能)。

这是通过某种堆栈实现的动态范围。当函数退出时,变量的状态、类型、属性和值与调用函数时一样被恢复(从堆栈中弹出)。

然而,在任何函数之外(在全局范围内),declare确实声明了一个变量,但如果之前设置过,则不会将其初始化为未设置(与declare在同一函数范围内第二次使用时相同)。如果指定了类型,则可以转换变量的值,尽管并非所有转换路径都被允许(仅标量到数组/散列),并且可以添加或删除属性。

In bash,在函数内declare -g作用于堆栈底部的变量,在最外层(“全局”)范围内。

declare -g从被激发ksh93typeset -g。但是ksh93实现静态范围,其中全局范围不同并且与每个函数范围分开。对动态范围做同样的事情毫无意义。在具有typeset -g( mksh, zsh, yash) 的所有其他 shell 中,typeset -g用于更改变量的某些属性而不实例化新的本地属性。

在 中bash,人们通常出于同样的目的使用它,但是因为它影响最外层作用域的变量而不是当前变量,所以它并不总是有效。

例如:

integer() { typeset -gi "$1"; }
Run Code Online (Sandbox Code Playgroud)

为了使变量的整数工作mksh/ yash/ zsh。它bash仅适用于未被调用者声明为本地的变量:

$ bash -c 'f() { declare a; integer a; a=1+1; echo "$a"; }; integer() { typeset -gi "$1"; }; f'
1+1
$ bash -c 'f() { integer a; a=1+1; echo "$a"; }; integer() { typeset -gi "$1"; }; f'
2
Run Code Online (Sandbox Code Playgroud)

请注意,export var既不是 也不typeset -x vartypeset -gx varexport如果变量已经存在,它会在不声明新变量的情况下添加属性。readonlyvs相同typeset -r

还要注意,unsetbash只有清除一个变量,如果它已经在当前范围内声明(叶子它宣称,虽然除了在全球范围内,它会删除属性和值和变量不再是数组或散列;还要注意,在namerefs,它取消设置引用的变量)。否则,它只是从上面提到的堆栈中弹出一个可变层。对于bash5.0 或更高版本,可以通过设置localvar_unset选项来修复。

所以总结一下:

 declare var
Run Code Online (Sandbox Code Playgroud)

当在函数中调用并且如果var之前未在同一函数中声明时,声明一个没有属性的标量类型的变量,并且最初未设置。

如果在任何函数之外调用或者如果var已经在同一个函数中声明,则没有任何效果,因为我们没有指定任何新类型或属性。

declare -g var
Run Code Online (Sandbox Code Playgroud)

无论它在哪里被调用,都会var在最外层(“全局”)范围内声明 a :使其声明scalar类型,没有属性,如果它以前在该范围内未知,则没有值(对于所有意图和目的来说,这与一个未知变量,除非它会显示在 ) 的输出中typeset -p,否则什么都不做。

无论如何,您可能无法在运行该命令的上下文中访问该变量:

f() { local a; g; }; g() { typeset -g a=123; echo "$a"; }; f
Run Code Online (Sandbox Code Playgroud)

什么都不输出。

  • 是的,就像我说的那样,`type` 和 `attribute` 之间的区别很模糊。你可以说数组/哈希是不能像只读那样删除的属性。在 ksh88 中,标量只是只包含索引 0 元素的数组。它在 `bash` 中非常相似,但是如果你执行 `a=1` 和 `a[0]=1`,`bash` 仍然会创建两个不同的变量。`zsh`/`yash` 是标量和数组类型非常不同的类型。 (2认同)

Jef*_*ler 1

declare name
Run Code Online (Sandbox Code Playgroud)

来自https://www.gnu.org/software/bash/manual/bashref.html#Bash-Builtins

声明变量并赋予它们属性。

您尚未提供任何选项,因此未分配任何属性,并且name已创建变量。未提供任何值,因此未分配任何值。

declare -g
Run Code Online (Sandbox Code Playgroud)

-g 选项强制在全局范围内创建或修改变量,即使在 shell 函数中执行 statements 时也是如此。在所有其他情况下都会忽略它。

由于您没有提供 a name,因此不会创建任何变量,因此它会被忽略。你自己看:

declare -g > foo
declare > bar
diff foo bar
Run Code Online (Sandbox Code Playgroud)

唯一的区别可能是 Bash 变量的值$_,并且可能PIPESTATUS基于您之前的命令;返回的变量列表没有其他区别。