从https://unix.stackexchange.com/a/381782/674
例如:
integer() { typeset -gi "$1"; }
要使变量成为整数,可以使用
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
请注意,
export var
既不是typeset -x var
也不是typeset -gx var
。export
如果变量已经存在,它会添加属性而不声明新变量。与readonly
vs相同typeset -r
。
对于bash,
在
f
第一个示例中,integer a
声明a
与a
内部声明不同的内容f
或使a
内部声明f
具有全局作用域有何作用?为什么会输出1+1
?在
f
第二个示例中,是否integer a
声明a
了全局范围?为什么会输出2
?
对于 zsh,同样的问题,除了为什么第一个示例输出2
而不是1+1
bash?
我说得对吗
- bash 和 zsh 都使用动态作用域,至少在示例中是这样?
-g
bash 和 zsh 中的选项typeset
意味着声明一个具有全局作用域的不存在的变量,或者更改一个现有的变量以具有全局作用域?
谢谢。
答案1
在编程语言中静态范围,例如大多数其他编程语言(如 C),
每个函数都有一个全局作用域和一个局部作用域。函数中出现的变量要么是函数私有的,要么是全局的。
一个函数只能访问其中一个它是局部变量或全局变量。除了通过引用传递之外,它无法访问另一个函数(甚至是其调用者)的变量。
在编程语言中动态范围界定,
函数可以看到其调用者的变量,并且该函数的调用树中的每个函数都有一个作用域。范围界定就像俄罗斯套娃,变量堆叠在另一个之上。
在该范围堆栈中,全球的作用域的唯一特殊之处在于它是最底部的作用域,但如果函数已被调用树中的任何函数将其屏蔽为局部变量,则函数不一定会看到该作用域中的变量。所以不存在一个全局作用域和一个局部作用域。
了解这里的历史很有帮助。
1.ksh93
在
ksh93
,一个函数,至少一个使用 ksh 语法声明的函数 (function f {...}
),如下静态范围。typeset
在函数中声明的变量是该函数的局部变量。a=global_a function f { typeset a=f_a g } function g { echo "$a" } f
会输出
global_a
.typeset -i var
在函数中改变了类型当地的var
变量,如果尚未在函数作用域中实例化它,则将其实例化。在
ksh93
,使用 Bourne 语法声明的函数 (f() {...}
)根本不进行范围界定。在这方面,该函数的代码看起来就像嵌入式在函数的调用者中,因此其中出现的任何变量都将具有与调用者相同的作用域,因此对于在其调用树中使用 ksh 语法声明的最顶层函数来说,无论是全局变量还是局部变量。typeset
将在最顶层函数中声明变量(如果其调用树中没有 ksh 语法函数,则在全局范围内声明变量)。例如,由于在 ksh-syntax 函数中,所有变量都是私有的或全局的,要实现我们的
integer
as inbash
,我们需要这样做:integer() { typeset -i "$1"; }
那是使用 Bourne 函数语法这根本不做范围界定。
或者使用 ksh 语法:
function integer { typeset -i "$1"; }
但要被调用为:
. integer var
(也就是说,通过使用
.
,整数代码在调用者的上下文中解释,就像您在脚本上调用.
( ) 时一样。source
或者使用ksh 语法:
function integer { typeset -ni "$1"; }
在哪里该变量作为引用传递就像
-n
您在 C 或大多数其他编程语言中所做的那样。
2. 所有其他类似 Bourne 的 shell
所有其他类似 Bourne 的 shell(包括 ksh88、ksh93 是完全重写和静态作用域的更改是(至少被认为是)功能包含在 POSIX 标准中的先决条件) 实施动态范围界定。
在函数中用
typeset
without声明的变量具有该函数的局部作用域。-g
例如,
typeset -i var
将声明变量 local(在当前函数范围内)并设置整数属性。例如,在顶部的代码中,它们都会输出
f_a
.也就是说,g
确实看到了 的局部变量f
。再举个例子,如果
f
调用g
则调用h
.如果h
不声明var
变量当地的在其作用域中,它将看到g
's 变量,或者可能看到f
's (如果g
尚未声明为var
本地变量),或者可能看到来自最底部作用域的变量。在所有
bash
,zsh
,yash
,mksh
,可以使用以下命令更改函数中变量的类型或值,而无需使其成为该函数的本地变量typeset -g
。就像integer
您引用的示例中的该函数一样 。但根据外壳的不同,它的做法也有所不同。在
mksh
,yash
,zsh
,typeset -g
不影响最底部的变量(全球的) 范围,但是 在当前定义的范围内。例如, while
typeset -i var
将声明变量 local(在当前函数作用域中)并设置整数属性,typeset -gi var
只执行后一部分(不影响 的范围var
)。例如,当函数调用
integer
上面的函数将整数属性添加到变量时,如下所示:f() { local myvar integer myvar ... }
它确实想
integer
改变的属性它是myvar
变量,而不是它不知道并且可能无法访问的全局范围内的变量。在
bash
,typeset -g
影响变量的实例在全局(最底层)范围内。虽然这就是 的含义g
,但它在具有动态作用域(如bash
.例如,在你问题的第一个例子中,
1+1
输出表明整数属性尚未添加到变量中。它已被添加到A全局范围内的变量 ,但不是函数有权访问的a
变量。f